Vector products

A vector is defined as having three dimensions as being represented by an ordered collection of three numbers:   (X, Y, Z).

If you imagine a graph with the   '''x'''   and   '''y'''   axis being at right angles to each other and having a third,   '''z'''   axis coming out of the page, then a triplet of numbers,   (X, Y, Z)   would represent a point in the region,   and a vector from the origin to the point.

Given the vectors:
         A = (a1,  a2,  a3)
 B = (b1,  b2,  b3)
 C = (c1,  c2,  c3)
then the following common vector products are defined:
* '''The dot product'''       (a scalar quantity)
::::  A * B = a1b1   +   a2b2   +    a3b3
* '''The cross product'''       (a vector quantity)
::::  A x B = (a2b3   -   a3b2,     a3b1   -   a1b3,     a1b2   -   a2b1)
* '''The scalar triple product'''       (a scalar quantity)
::::  A * (B x C)
* '''The vector triple product'''       (a vector quantity)
::::  A x (B x C)

;Task:
Given the three vectors:
         a = ( 3,    4,    5)
         b = ( 4,    3,    5)
         c = (-5,  -12,  -13)
# Create a named function/subroutine/method to compute the dot product of two vectors.
# Create a function to compute the cross product of two vectors.
# Optionally create a function to compute the scalar triple product of three vectors.
# Optionally create a function to compute the vector triple product of three vectors.
# Compute and display: a * b
# Compute and display: a x b
# Compute and display: a * (b x c), the scalar triple product.
# Compute and display: a x (b x c), the vector triple product.

;References:
*   A starting page on Wolfram MathWorld is   {{Wolfram|Vector|Multiplication}}.
*   Wikipedia   dot product.
*   Wikipedia   cross product.
*   Wikipedia   triple product.

;Related tasks:
*   [[Dot product]]
*   [[Quaternion type]]

Solution:
Vector = {}
function Vector.new( _x, _y, _z )
    return { x=_x, y=_y, z=_z }
end

function Vector.dot( A, B )
    return A.x*B.x + A.y*B.y + A.z*B.z
end

function Vector.cross( A, B )
    return { x = A.y*B.z - A.z*B.y,
             y = A.z*B.x - A.x*B.z,
             z = A.x*B.y - A.y*B.x }
end

function Vector.scalar_triple( A, B, C )
    return Vector.dot( A, Vector.cross( B, C ) )
end

function Vector.vector_triple( A, B, C )
    return Vector.cross( A, Vector.cross( B, C ) )
end

A = Vector.new( 3, 4, 5 )
B = Vector.new( 4, 3, 5 )
C = Vector.new( -5, -12, -13 )

print( Vector.dot( A, B ) )

r = Vector.cross(A, B )
print( r.x, r.y, r.z )

print( Vector.scalar_triple( A, B, C ) )

r = Vector.vector_triple( A, B, C )
print( r.x, r.y, r.z )


Video display modes

The task is to demonstrate how to switch video display modes within the language. A brief description of the supported video modes would be useful.

Solution:
print("\33[?3h") -- 132-column text
print("\33[?3l") -- 80-column text


Visualize a tree

A tree structure   (i.e. a rooted, connected acyclic graph)   is often used in programming.

It's often helpful to visually examine such a structure.

There are many ways to represent trees to a reader, such as:
:::*   indented text   (a la unix  tree  command)
:::*   nested HTML tables
:::*   hierarchical GUI widgets
:::*   2D   or   3D   images
:::*   etc.

;Task:
Write a program to produce a visual representation of some tree.

The content of the tree doesn't matter, nor does the output format, the only requirement being that the output is human friendly.

Make do with the vague term "friendly" the best you can.

Solution:
function makeTree(v,ac)
    if type(ac) == "table" then
        return {value=v,children=ac}
    else
        return {value=v}
    end
end

function printTree(t,last,prefix)
    if last == nil then
        printTree(t, false, '')
    else
        local current = ''
        local next = ''

        if last then
            current = prefix .. '\\-' .. t.value
            next = prefix .. '  '
        else
            current = prefix .. '|-' .. t.value
            next = prefix .. '| '
        end

        print(current:sub(3))
        if t.children ~= nil then
            for k,v in pairs(t.children) do
                printTree(v, k == #t.children, next)
            end
        end
    end
end

printTree(
    makeTree('A', {
        makeTree('B0', {
            makeTree('C1'),
            makeTree('C2', {
                makeTree('D', {
                    makeTree('E1'),
                    makeTree('E2'),
                    makeTree('E3')
                })
            }),
            makeTree('C3', {
                makeTree('F1'),
                makeTree('F2'),
                makeTree('F3', {makeTree('G')}),
                makeTree('F4', {
                    makeTree('H1'),
                    makeTree('H2')
                })
            })
        }),
        makeTree('B1',{
            makeTree('K1'),
            makeTree('K2', {
                makeTree('L1', {makeTree('M')}),
                makeTree('L2'),
                makeTree('L3')
            }),
            makeTree('K3')
        })
    })
)


Vogel's approximation method

Vogel's Approximation Method (VAM) is a technique for finding a good initial feasible solution to an allocation problem.

The powers that be have identified 5 tasks that need to be solved urgently. Being imaginative chaps, they have called them "A", "B", "C", "D", and "E". They estimate that:
* A will require 30 hours of work,
* B will require 20 hours of work,
* C will require 70 hours of work,
* D will require 30 hours of work, and
* E will require 60 hours of work.

They have identified 4 contractors willing to do the work, called "W", "X", "Y", and "Z".
* W has 50 hours available to commit to working,
* X has 60 hours available,
* Y has 50 hours available, and
* Z has 50 hours available.
The cost per hour for each contractor for each task is summarized by the following table:

   A  B  C  D  E
W 16 16 13 22 17
X 14 14 13 19 15
Y 19 19 20 23 50
Z 50 12 50 15 11

The task is to use VAM to allocate contractors to tasks. It scales to large problems, so ideally keep sorts out of the iterative cycle. It works as follows:

:Step 1: Balance the given transportation problem if either (total supply>total demand) or (total supply
        A       B       C       D       E       W       X       Y       Z
1       2       2       0       4       4       3       1       0       1   E-Z(50)

Determine the largest difference (D or E above). In the case of ties I shall choose the one with the lowest price (in this case E because the lowest price for D is Z=15, whereas for E it is Z=11). For your choice determine the minimum cost (chosen E above so Z=11 is chosen now). Allocate as much as possible from Z to E (50 in this case limited by Z's supply).
Adjust the supply and demand accordingly. If demand or supply becomes 0 for a given task or contractor it plays no further part. In this case Z is out of it. If you choose arbitrarily, and chose D see here for the working.

Repeat until all supply and demand is met:

2       2       2       0       3       2       3       1       0       -   C-W(50)
3       5       5       7       4      35       -       1       0       -   E-X(10)
4       5       5       7       4       -       -       1       0       -   C-X(20)
5       5       5       -       4       -       -       0       0       -   A-X(30)
6       -      19       -      23       -       -       -       4       -   D-Y(30)
        -       -       -       -       -       -       -       -       -   B-Y(20)

Finally calculate the cost of your solution. In the example given it is PS3100:

   A  B  C  D  E
W       50
X 30    20    10
Y    20    30
Z             50

The optimal solution determined by GLPK is PS3100:

   A  B  C  D  E
W       50
X 10 20 20    10
Y 20       30
Z             50

;Cf.
* Transportation problem

Solution:
function initArray(n,v)
    local tbl = {}
    for i=1,n do
        table.insert(tbl,v)
    end
    return tbl
end

function initArray2(m,n,v)
    local tbl = {}
    for i=1,m do
        table.insert(tbl,initArray(n,v))
    end
    return tbl
end

supply = {50, 60, 50, 50}
demand = {30, 20, 70, 30, 60}
costs = {
    {16, 16, 13, 22, 17},
    {14, 14, 13, 19, 15},
    {19, 19, 20, 23, 50},
    {50, 12, 50, 15, 11}
}

nRows = table.getn(supply)
nCols = table.getn(demand)

rowDone = initArray(nRows, false)
colDone = initArray(nCols, false)
results = initArray2(nRows, nCols, 0)

function diff(j,le,isRow)
    local min1 = 100000000
    local min2 = min1
    local minP = -1
    for i=1,le do
        local done = false
        if isRow then
            done = colDone[i]
        else
            done = rowDone[i]
        end
        if not done then
            local c = 0
            if isRow then
                c = costs[j][i]
            else
                c = costs[i][j]
            end
            if c < min1 then
                min2 = min1
                min1 = c
                minP = i
            elseif c < min2 then
                min2 = c
            end
        end
    end
    return {min2 - min1, min1, minP}
end

function maxPenalty(len1,len2,isRow)
    local md = -100000000
    local pc = -1
    local pm = -1
    local mc = -1

    for i=1,len1 do
        local done = false
        if isRow then
            done = rowDone[i]
        else
            done = colDone[i]
        end
        if not done then
            local res = diff(i, len2, isRow)
            if res[1] > md then
                md = res[1] -- max diff
                pm = i      -- pos of max diff
                mc = res[2] -- min cost
                pc = res[3] -- pos of min cost
            end
        end
    end

    if isRow then
        return {pm, pc, mc, md}
    else
        return {pc, pm, mc, md}
    end
end

function nextCell()
    local res1 = maxPenalty(nRows, nCols, true)
    local res2 = maxPenalty(nCols, nRows, false)
    if res1[4] == res2[4] then
        if res1[3] < res2[3] then
            return res1
        else
            return res2
        end
    else
        if res1[4] > res2[4] then
            return res2
        else
            return res1
        end
    end
end

function main()
    local supplyLeft = 0
    for i,v in pairs(supply) do
        supplyLeft = supplyLeft + v
    end
    local totalCost = 0
    while supplyLeft > 0 do
        local cell = nextCell()
        local r = cell[1]
        local c = cell[2]
        local q = math.min(demand[c], supply[r])
        demand[c] = demand[c] - q
        if demand[c] == 0 then
            colDone[c] = true
        end
        supply[r] = supply[r] - q
        if supply[r] == 0 then
            rowDone[r] = true
        end
        results[r][c] = q
        supplyLeft = supplyLeft - q
        totalCost = totalCost + q * costs[r][c]
    end

    print("    A   B   C   D   E")
    local labels = {'W','X','Y','Z'}
    for i,r in pairs(results) do
        io.write(labels[i])
        for j,c in pairs(r) do
            io.write(string.format("  %2d", c))
        end
        print()
    end
    print("Total Cost = " .. totalCost)
end

main()


Voronoi diagram

A Voronoi diagram is a diagram consisting of a number of sites.

Each Voronoi site ''s''  also has a Voronoi cell consisting of all points closest to ''s''.

;Task:
Demonstrate how to generate and display a Voroni diagram.

See algo [[K-means++ clustering]].

Solution:
function love.load( )
	love.math.setRandomSeed( os.time( ) ) --set the random seed
	keys = { } --an empty table where we will store key presses
	number_cells = 50 --the number of cells we want in our diagram
	--draw the voronoi diagram to a canvas
	voronoiDiagram = generateVoronoi( love.graphics.getWidth( ), love.graphics.getHeight( ), number_cells )
end

function hypot( x, y )
	return math.sqrt( x*x + y*y )
end

function generateVoronoi( width, height, num_cells )
	canvas = love.graphics.newCanvas( width, height )
	local imgx = canvas:getWidth( )
	local imgy = canvas:getHeight( )
	local nx = { }
	local ny = { }
	local nr = { }
	local ng = { }
	local nb = { }
	for a = 1, num_cells do
	table.insert( nx, love.math.random( 0, imgx ) )
	table.insert( ny, love.math.random( 0, imgy ) )
	table.insert( nr, love.math.random( 0, 1 ) )
	table.insert( ng, love.math.random( 0, 1 ) )
	table.insert( nb, love.math.random( 0, 1 ) )
	end
	love.graphics.setColor( { 1, 1, 1 } )
	love.graphics.setCanvas( canvas )
	for y = 1, imgy do
	for x = 1, imgx do
			dmin = hypot( imgx-1, imgy-1 )
		j = -1
		for i = 1, num_cells do
		d = hypot( nx[i]-x, ny[i]-y )
		if d < dmin then
	 	    dmin = d
			j = i
		end
		end
		love.graphics.setColor( { nr[j], ng[j], nb[j] } )
		love.graphics.points( x, y )
	end
	end
	--reset color
	love.graphics.setColor( { 1, 1, 1 } )
	--draw points
	for b = 1, num_cells do
	love.graphics.points( nx[b], ny[b] )
	end
	love.graphics.setCanvas( )
	return canvas
end

--RENDER
function love.draw( )
	--reset color
	love.graphics.setColor( { 1, 1, 1 } )
	--draw diagram
	love.graphics.draw( voronoiDiagram )
	--draw drop shadow text
	love.graphics.setColor( { 0, 0, 0 } )
	love.graphics.print( "space: regenerate\nesc: quit", 1, 1 )
	--draw text
	love.graphics.setColor( { 0.7, 0.7, 0 } )
	love.graphics.print( "space: regenerate\nesc: quit" )
end

--CONTROL
function love.keyreleased( key )
	if key == 'space' then
	voronoiDiagram = generateVoronoi( love.graphics.getWidth( ), love.graphics.getHeight( ), number_cells )
	elseif key == 'escape' then
	love.event.quit( )
	end
end


Water collected between towers

In a two-dimensional world, we begin with any bar-chart (or row of close-packed 'towers', each of unit width), and then it rains,
completely filling all convex enclosures in the chart with water.

9               ##           9               ##
8               ##           8               ##
7     ##        ##           7     ####
6     ##  ##    ##           6     ######
5 ##  ##  ##  ####           5 ##########
4 ##  ##  ########           4 ############
3 ######  ########           3 ##############
2 ################  ##       2 ##################
1 ####################       1 ####################

In the example above, a bar chart representing the values [5, 3, 7, 2, 6, 4, 5, 9, 1, 2] has filled, collecting 14 units of water.

Write a function, in your language, from a given array of heights, to the number of water units that can be held in this way, by a corresponding bar chart.

Calculate the number of water units that could be collected by bar charts representing each of the following seven series:

   [[1, 5, 3, 7, 2],
    [5, 3, 7, 2, 6, 4, 5, 9, 1, 2],
    [2, 6, 3, 5, 2, 8, 1, 4, 2, 2, 5, 3, 5, 7, 4, 1],
    [5, 5, 5, 5],
    [5, 6, 7, 8],
    [8, 7, 7, 6],
    [6, 7, 10, 7, 6]]

See, also:

* Four Solutions to a Trivial Problem - a Google Tech Talk by Guy Steele
* Water collected between towers on Stack Overflow, from which the example above is taken)
* An interesting Haskell solution, using the Tardis monad, by Phil Freeman in a Github gist.

Solution:
function waterCollected(i,tower)
    local length = 0
    for _ in pairs(tower) do
        length = length + 1
    end

    local wu = 0
    repeat
        local rht = length - 1
        while rht >= 0 do
            if tower[rht + 1] > 0 then
                break
            end
            rht = rht - 1
        end
        if rht < 0 then
            break
        end

        local bof = 0
        local col = 0
        while col <= rht do
            if tower[col + 1] > 0 then
                tower[col + 1] = tower[col + 1] - 1
                bof = bof + 1
            elseif bof > 0 then
                wu = wu + 1
            end
            col = col + 1
        end
        if bof < 2 then
            break
        end
    until false
    if wu == 0 then
        print(string.format("Block %d does not hold any water.", i))
    else
        print(string.format("Block %d holds %d water units.", i, wu))
    end
end

function main()
    local towers = {
        {1, 5, 3, 7, 2},
        {5, 3, 7, 2, 6, 4, 5, 9, 1, 2},
        {2, 6, 3, 5, 2, 8, 1, 4, 2, 2, 5, 3, 5, 7, 4, 1},
        {5, 5, 5, 5},
        {5, 6, 7, 8},
        {8, 7, 7, 6},
        {6, 7, 10, 7, 6}
    }

    for i,tbl in pairs(towers) do
        waterCollected(i,tbl)
    end
end

main()


Weird numbers

In number theory, a perfect either).

In other words, the sum of the proper divisors of the number (divisors including 1 but not itself) is greater than the number itself (the number is ''abundant''), but no subset of those divisors sums to the number itself (the number is not ''semiperfect'').

For example:

* '''12''' is ''not'' a weird number.
** It is abundant; its proper divisors '''1, 2, 3, 4, 6''' sum to '''16''' (which ''is'' > 12),
** but it ''is'' semiperfect, e.g.:     '''6 + 4 + 2 == 12'''.
* '''70''' ''is'' a weird number.
** It is abundant; its proper divisors '''1, 2, 5, 7, 10, 14, 35''' sum to '''74''' (which ''is'' > 70),
** and there is no subset of proper divisors that sum to '''70'''.

;Task:
Find and display, here on this page, the first '''25''' weird numbers.

;Related tasks:
:* Abundant, deficient and perfect number classifications
:* Proper divisors

;See also:
:* OEIS: A006037 weird numbers
:* Wikipedia: weird number
:* MathWorld: weird number

Solution:
function make(n, d)
    local a = {}
    for i=1,n do
        table.insert(a, d)
    end
    return a
end

function reverse(t)
    local n = #t
    local i = 1
    while i < n do
        t[i],t[n] = t[n],t[i]
        i = i + 1
        n = n - 1
    end
end

function tail(list)
    return { select(2, unpack(list)) }
end

function divisors(n)
    local divs = {}
    table.insert(divs, 1)

    local divs2 = {}

    local i = 2
    while i * i <= n do
        if n % i == 0 then
            local j = n / i
            table.insert(divs, i)
            if i ~= j then
                table.insert(divs2, j)
            end
        end
        i = i + 1
    end

    reverse(divs)
    for i,v in pairs(divs) do
        table.insert(divs2, v)
    end
    return divs2
end

function abundant(n, divs)
    local sum = 0
    for i,v in pairs(divs) do
        sum = sum + v
    end
    return sum > n
end

function semiPerfect(n, divs)
    if #divs > 0 then
        local h = divs[1]
        local t = tail(divs)
        if n < h then
            return semiPerfect(n, t)
        else
            return n == h
                or semiPerfect(n - h, t)
                or semiPerfect(n, t)
        end
    else
        return false
    end
end

function sieve(limit)
    -- false denotes abundant and not semi-perfect.
    -- Only interested in even numbers >= 2
    local w = make(limit, false)
    local i = 2
    while i < limit do
        if not w[i] then
            local divs = divisors(i)
            if not abundant(i, divs) then
                w[i] = true
            elseif semiPerfect(i, divs) then
                local j = i
                while j < limit do
                    w[j] = true
                    j = j + i
                end
            end
        end
        i = i + 1
    end
    return w
end

function main()
    local w = sieve(17000)
    local count = 0
    local max = 25
    print("The first 25 weird numbers:")
    local n = 2
    while count < max do
        if not w[n] then
            io.write(n, ' ')
            count = count + 1
        end
        n = n + 2
    end
    print()
end

main()


Word frequency

Given a text file and an integer   '''n''',   print/display the   '''n'''   most
common words in the file   (and the number of their occurrences)   in decreasing frequency.

For the purposes of this task:
*   A word is a sequence of one or more contiguous letters.
*   You are free to define what a   ''letter''   is.
*   Underscores, accented letters, apostrophes, hyphens, and other special characters can be handled at your discretion.
*   You may treat a compound word like   '''well-dressed'''   as either one word or two.
*   The word   '''it's'''   could also be one or two words as you see fit.
*   You may also choose not to support non US-ASCII characters.
*   Assume words will not span multiple lines.
*   Don't worry about normalization of word spelling differences.
*   Treat   '''color'''   and   '''colour'''   as two distinct words.
*   Uppercase letters are considered equivalent to their lowercase counterparts.
*   Words of equal frequency can be listed in any order.
*   Feel free to explicitly state the thoughts behind the program decisions.

Show example output using Les Miserables from Project Gutenberg as the text file input and display the top   '''10'''   most used words.

;History:
This task was originally taken from programming pearls from Communications of the ACM June 1986 Volume 29 Number 6
where this problem is solved by Donald Knuth using literate programming and then critiqued by Doug McIlroy,
demonstrating solving the problem in a 6 line Unix shell script (provided as an example below).

;References:
*McIlroy's program

Solution:
-- This program takes two optional command line arguments.  The first (arg[1])
-- specifies the input file, or defaults to standard input.  The second
-- (arg[2]) specifies the number of results to show, or defaults to 10.

-- in freq, each key is a word and each value is its count
local freq = {}
for line in io.lines(arg[1]) do
	-- %a stands for any letter
	for word in string.gmatch(string.lower(line), "%a+") do
		if not freq[word] then
			freq[word] = 1
		else
			freq[word] = freq[word] + 1
		end
	end
end

-- in array, each entry is an array whose first value is the count and whose
-- second value is the word
local array = {}
for word, count in pairs(freq) do
	table.insert(array, {count, word})
end
table.sort(array, function (a, b) return a[1] > b[1] end)

for i = 1, arg[2] or 10 do
	io.write(string.format('%7d %s\n', array[i][1] , array[i][2]))
end


Word wrap

Even today, with proportional fonts and complex layouts, there are still cases where you need to wrap text at a specified column.

;Basic task:
The basic task is to wrap a paragraph of text in a simple way in your language.

If there is a way to do this that is built-in, trivial, or provided in a standard library, show that.  Otherwise implement the minimum length greedy algorithm from Wikipedia.

Show your routine working on a sample of text at two different wrap columns.

;Extra credit:
Wrap text using a more sophisticated algorithm such as the Knuth and Plass TeX algorithm.
If your language provides this, you get easy extra credit,
but you ''must reference documentation'' indicating that the algorithm
is something better than a simple minimum length algorithm.

If you have both basic and extra credit solutions, show an example where
the two algorithms give different results.

Solution:
function splittokens(s)
    local res = {}
    for w in s:gmatch("%S+") do
        res[#res+1] = w
    end
    return res
end

function textwrap(text, linewidth)
    if not linewidth then
        linewidth = 75
    end

    local spaceleft = linewidth
    local res = {}
    local line = {}

    for _, word in ipairs(splittokens(text)) do
        if #word + 1 > spaceleft then
            table.insert(res, table.concat(line, ' '))
            line = {word}
            spaceleft = linewidth - #word
        else
            table.insert(line, word)
            spaceleft = spaceleft - (#word + 1)
        end
    end

    table.insert(res, table.concat(line, ' '))
    return table.concat(res, '\n')
end

local example1 = [[
Even today, with proportional fonts and complex layouts,
there are still cases where you need to wrap text at a
specified column. The basic task is to wrap a paragraph
of text in a simple way in your language. If there is a
way to do this that is built-in, trivial, or provided in
a standard library, show that. Otherwise implement the
minimum length greedy algorithm from Wikipedia.
]]

print(textwrap(example1))
print()
print(textwrap(example1, 60))


World Cup group stage

It's World Cup season (or at least it was when this page was created)!

The World Cup is an international football/soccer tournament that happens every 4 years.   Countries put their international teams together in the years between tournaments and qualify for the tournament based on their performance in other international games.   Once a team has qualified they are put into a group with 3 other teams.

For the first part of the World Cup tournament the teams play in "group stage" games where each of the four teams in a group plays all three other teams once.   The results of these games determine which teams will move on to the "knockout stage" which is a standard single-elimination tournament.   The two teams from each group with the most standings points move on to the knockout stage.

Each game can result in a win for one team and a loss for the other team or it can result in a draw/tie for each team.
:::*   A win is worth three points.
:::*   A draw/tie is worth one point.
:::*   A loss is worth zero points.

;Task:
:*   Generate all possible outcome combinations for the six group stage games.   With three possible outcomes for each game there should be 36 = 729 of them.
:*   Calculate the standings points for each team with each combination of outcomes.
:*   Show a histogram (graphical,   ASCII art, or straight counts--whichever is easiest/most fun) of the standings points for all four teams over all possible outcomes.

Don't worry about tiebreakers as they can get complicated.   We are basically looking to answer the question "if a team gets x standings points, where can they expect to end up in the group standings?".

''Hint: there should be no possible way to end up in second place with less than two points as well as no way to end up in first with less than three.   Oddly enough, there is no way to get 8 points at all.''

Solution:
function array1D(a, d)
    local m = {}
    for i=1,a do
        table.insert(m, d)
    end
    return m
end

function array2D(a, b, d)
    local m = {}
    for i=1,a do
        table.insert(m, array1D(b, d))
    end
    return m
end

function fromBase3(num)
    local out = 0
    for i=1,#num do
        local c = num:sub(i,i)
        local d = tonumber(c)
        out = 3 * out + d
    end
    return out
end

function toBase3(num)
    local ss = ""
    while num > 0 do
        local rem = num % 3
        num = math.floor(num / 3)
        ss = ss .. tostring(rem)
    end
    return string.reverse(ss)
end

games = { "12", "13", "14", "23", "24", "34" }

results = "000000"
function nextResult()
    if results == "222222" then
        return false
    end

    local res = fromBase3(results)
    results = string.format("%06s", toBase3(res + 1))

    return true
end

points = array2D(4, 10, 0)

repeat
    local records = array1D(4, 0)

    for i=1,#games do
        if results:sub(i,i) == '2' then
            local j = tonumber(games[i]:sub(1,1))
            records[j] = records[j] + 3
        elseif results:sub(i,i) == '1' then
            local j = tonumber(games[i]:sub(1,1))
            records[j] = records[j] + 1
            j = tonumber(games[i]:sub(2,2))
            records[j] = records[j] + 1
        elseif results:sub(i,i) == '0' then
            local j = tonumber(games[i]:sub(2,2))
            records[j] = records[j] + 3
        end
    end

    table.sort(records)
    for i=1,#records do
        points[i][records[i]+1] = points[i][records[i]+1] + 1
    end
until not nextResult()

print("POINTS       0    1    2    3    4    5    6    7    8    9")
print("-------------------------------------------------------------")
places = { "1st", "2nd", "3rd", "4th" }
for i=1,#places do
    io.write(places[i] .. " place")
    local row = points[i]
    for j=1,#row do
        io.write(string.format("%5d", points[5 - i][j]))
    end
    print()
end


Write float arrays to a text file

Write two equal-sized numerical arrays 'x' and 'y' to
a two-column text file named 'filename'.

The first column of the file contains values from an 'x'-array with a
given 'xprecision', the second -- values from 'y'-array with 'yprecision'.

For example, considering:
    x = {1, 2, 3, 1e11};
    y = {1, 1.4142135623730951, 1.7320508075688772, 316227.76601683791};
                                                           /* sqrt(x) */
    xprecision = 3;
    yprecision = 5;

The file should look like:
    1    1
    2    1.4142
    3    1.7321
    1e+011   3.1623e+005

This task is intended as a subtask for [[Measure relative performance of sorting algorithms implementations]].

Solution:
filename = "file.txt"

x = { 1, 2, 3, 1e11 }
y = { 1, 1.4142135623730951, 1.7320508075688772, 316227.76601683791 };
xprecision = 3;
yprecision = 5;

fstr = "%."..tostring(xprecision).."f ".."%."..tostring(yprecision).."f\n"

fp = io.open( filename, "w+" )

for i = 1, #x do
    fp:write( string.format( fstr, x[i], y[i] ) )
end

io.close( fp )


Write language name in 3D ASCII

Write/display a language's name in '''3D''' ASCII.

(We can leave the definition of "3D ASCII" fuzzy,
so long as the result is interesting or amusing,
not a cheap hack to satisfy the task.)

;Related tasks:
* draw a sphere
* draw a cuboid
* draw a rotating cube
* draw a Deathstar

Solution:
io.write(" /$$\n")
io.write("| $$\n")
io.write("| $$       /$$   /$$  /$$$$$$\n")
io.write("| $$      | $$  | $$ |____  $$\n")
io.write("| $$      | $$  | $$  /$$$$$$$\n")
io.write("| $$      | $$  | $$ /$$__  $$\n")
io.write("| $$$$$$$$|  $$$$$$/|  $$$$$$$\n")
io.write("|________/ \______/  \_______/\n")


Yellowstone sequence

The '''Yellowstone sequence''', also called the '''Yellowstone permutation''', is defined as:

For n <= 3,

    a(n) = n

For n >= 4,

    a(n) = the smallest number not already in sequence such that a(n) is relatively prime to a(n-1) and
           is not relatively prime to a(n-2).

The sequence is a permutation of the natural numbers, and gets its name from what its authors felt was a spiking, geyser like appearance of a plot of the sequence.

;Example:
a(4) is 4 because 4 is the smallest number following 1, 2, 3 in the sequence that is relatively prime to the entry before it (3), and is not relatively prime to the number two entries before it (2).

;Task
: Find and show as output the first  '''30'''  Yellowstone numbers.

;Extra
:  Demonstrate how to plot, with x = n and y coordinate a(n), the first 100 Yellowstone numbers.

;Related tasks:
:*   Greatest common divisor.
:*   Plot coordinate pairs.

;See also:
:*   The OEIS entry:   A098550 The Yellowstone permutation.
:*   Applegate et al, 2015: The Yellowstone Permutation [https://arxiv.org/abs/1501.01669].

Solution:
function gcd(a, b)
    if b == 0 then
        return a
    end
    return gcd(b, a % b)
end

function printArray(a)
    io.write('[')
    for i,v in pairs(a) do
        if i > 1 then
            io.write(', ')
        end
        io.write(v)
    end
    io.write(']')
    return nil
end

function removeAt(a, i)
    local na = {}
    for j,v in pairs(a) do
        if j ~= i then
            table.insert(na, v)
        end
    end
    return na
end

function yellowstone(sequenceCount)
    local yellow = {1, 2, 3}
    local num = 4
    local notYellow = {}
    local yellowSize = 3
    while yellowSize < sequenceCount do
        local found = -1
        for i,test in pairs(notYellow) do
            if gcd(yellow[yellowSize - 1], test) > 1 and gcd(yellow[yellowSize - 0], test) == 1 then
                found = i
                break
            end
        end
        if found >= 0 then
            table.insert(yellow, notYellow[found])
            notYellow = removeAt(notYellow, found)
            yellowSize = yellowSize + 1
        else
            while true do
                if gcd(yellow[yellowSize - 1], num) > 1 and gcd(yellow[yellowSize - 0], num) == 1 then
                    table.insert(yellow, num)
                    yellowSize = yellowSize + 1
                    num = num + 1
                    break
                end
                table.insert(notYellow, num)
                num = num + 1
            end
        end
    end
    return yellow
end

function main()
    print("First 30 values in the yellowstone sequence:")
    printArray(yellowstone(30))
    print()
end

main()


Zeckendorf number representation

Just as numbers can be represented in a positional notation as sums of multiples of the powers of ten (decimal) or two (binary); all the positive integers can be represented as the sum of one or zero times the distinct members of the Fibonacci series.

Recall that the first six distinct Fibonacci numbers are:  1, 2, 3, 5, 8, 13.

The decimal number eleven can be written as 0*13 + 1*8 + 0*5 + 1*3 + 0*2 + 0*1 or 010100 in positional notation where the columns represent multiplication by a particular member of the sequence. Leading zeroes are dropped so that 11 decimal becomes 10100.

10100 is not the only way to make 11 from the Fibonacci numbers however;  0*13 + 1*8 + 0*5 + 0*3 + 1*2 + 1*1 or 010011 would also represent decimal 11. For a true Zeckendorf number there is the added restriction that ''no two consecutive Fibonacci numbers can be used'' which leads to the former unique solution.

;Task:
Generate and show here a table of the Zeckendorf number representations of the decimal numbers zero to twenty, in order.

The intention in this task to find the Zeckendorf form of an arbitrary integer.  The Zeckendorf form can be iterated by some bit twiddling rather than calculating each value separately but leave that to another separate task.

;Also see:
*   OEIS A014417   for the the sequence of required results.
*   Brown's Criterion - Numberphile

;Related task:
*   [[Fibonacci sequence]]

Solution:
-- Return the distinct Fibonacci numbers not greater than 'n'
function fibsUpTo (n)
    local fibList, last, current, nxt = {}, 1, 1
    while current <= n do
        table.insert(fibList, current)
        nxt = last + current
        last = current
        current = nxt
    end
    return fibList
end

-- Return the Zeckendorf representation of 'n'
function zeckendorf (n)
    local fib, zeck = fibsUpTo(n), ""
    for pos = #fib, 1, -1 do
        if n >= fib[pos] then
            zeck = zeck .. "1"
            n = n - fib[pos]
        else
            zeck = zeck .. "0"
        end
    end
    if zeck == "" then return "0" end
    return zeck
end

-- Main procedure
print(" n\t| Zeckendorf(n)")
print(string.rep("-", 23))
for n = 0, 20 do
    print(" " .. n, "| " .. zeckendorf(n))
end


Zhang-Suen thinning algorithm

Solution:
function zhangSuenThin(img)
    local dirs={
        { 0,-1},
        { 1,-1},
        { 1, 0},
        { 1, 1},
        { 0, 1},
        {-1, 1},
        {-1, 0},
        {-1,-1},
        { 0,-1},
    }

    local black=1
    local white=0

    function A(x, y)
        local c=0
        local current=img[y+dirs[1][2]][x+dirs[1][1]]
        for i=2,#dirs do
            local to_compare=img[y+dirs[i][2]][x+dirs[i][1]]
            if current==white and to_compare==black then
                c=c+1
            end
            current=to_compare
        end
        return c
    end

    function B(x, y)
        local c=0
        for i=2,#dirs do
            local value=img[y+dirs[i][2]][x+dirs[i][1]]
            if value==black then
                c=c+1
            end
        end
        return c
    end

    function common_step(x, y)
        if img[y][x]~=black or x<=1 or x>=#img[y] or y<=1 or y>=#img then
            return false
        end

        local b_value=B(x, y)
        if b_value<2 or b_value>6 then
            return false
        end

        local a_value=A(x, y)
        if a_value~=1 then
            return false
        end
        return true
    end

    function step_one(x, y)
        if not common_step(x, y) then
            return false
        end
        local p2=img[y+dirs[1][2]][x+dirs[1][1]]
        local p4=img[y+dirs[3][2]][x+dirs[3][1]]
        local p6=img[y+dirs[5][2]][x+dirs[5][1]]
        local p8=img[y+dirs[7][2]][x+dirs[7][1]]

        if p4==white or p6==white or p2==white and p8==white then
            return true
        end
        return false
    end

    function step_two(x, y)
        if not common_step(x, y) then
            return false
        end
        local p2=img[y+dirs[1][2]][x+dirs[1][1]]
        local p4=img[y+dirs[3][2]][x+dirs[3][1]]
        local p6=img[y+dirs[5][2]][x+dirs[5][1]]
        local p8=img[y+dirs[7][2]][x+dirs[7][1]]

        if p2==white or p8==white or p4==white and p6==white then
            return true
        end
        return false
    end

    function convert(to_do)
        for k,v in pairs(to_do) do
            img[v[2]][v[1]]=white
        end
    end

    function do_step_on_all(step)
        local to_convert={}
        for y=1,#img do
            for x=1,#img[y] do
                if step(x, y) then
                    table.insert(to_convert, {x,y})
                end
            end
        end
        convert(to_convert)
        return #to_convert>0
    end

    local continue=true
    while continue do
        continue=false
        if do_step_on_all(step_one) then
            continue=true
        end

        if do_step_on_all(step_two) then
            continue=true
        end
    end

    for y=1,#img do
        for x=1,#img[y] do
            io.write(img[y][x]==black and '#' or ' ')
        end
        io.write('\n')
    end
end

local image = {
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
    {0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0},
    {0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0},
    {0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {0,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0},
    {0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,0},
    {0,1,1,1,0,0,0,1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
}

zhangSuenThin(image)