Balanced ternary

Balanced ternary is a way of representing numbers.  Unlike the prevailing binary representation, a balanced ternary integer is in base 3, and each digit can have the values 1, 0, or -1.

;Examples:
Decimal 11 = 32 + 31 - 30, thus it can be written as "++-"

Decimal  6 = 32 - 31 + 0 x 30, thus it can be written as "+-0"

;Task:
Implement balanced ternary representation of integers with the following:
# Support arbitrarily large integers, both positive and negative;
# Provide ways to convert to and from text strings, using digits '+', '-' and '0' (unless you are already using strings to represent balanced ternary; but see requirement 5).
# Provide ways to convert to and from native integer type (unless, improbably, your platform's native integer type ''is'' balanced ternary).  If your native integers can't support arbitrary length, overflows during conversion must be indicated.
# Provide ways to perform addition, negation and multiplication directly on balanced ternary integers; do ''not'' convert to native integers first.
# Make your implementation efficient, with a reasonable definition of "efficient" (and with a reasonable definition of "reasonable").

'''Test case''' With balanced ternaries ''a'' from string "+-0++0+", ''b'' from native integer -436, ''c'' "+-++-":
* write out ''a'', ''b'' and ''c'' in decimal notation;
* calculate ''a'' x (''b'' - ''c''), write out the result in both ternary and decimal notations.

'''Note:''' The pages floating point balanced ternary.

Solution:
function to_bt(n)
    local d = { '0', '+', '-' }
    local v = { 0, 1, -1 }

    local b = ""

    while n ~= 0 do
        local r = n % 3
        if r < 0 then
            r = r + 3
        end

        b = b .. d[r + 1]

        n = n - v[r + 1]
        n = math.floor(n / 3)
    end

    return b:reverse()
end

function from_bt(s)
    local n = 0

    for i=1,s:len() do
        local c = s:sub(i,i)
        n = n * 3
        if c == '+' then
            n = n + 1
        elseif c == '-' then
            n = n - 1
        end
    end

    return n
end

function last_char(s)
    return s:sub(-1,-1)
end

function add(b1,b2)
    local out = "oops"
    if b1 ~= "" and b2 ~= "" then
        local d = ""

        local L1 = last_char(b1)
        local c1 = b1:sub(1,-2)
        local L2 = last_char(b2)
        local c2 = b2:sub(1,-2)
        if L2 < L1 then
            L2, L1 = L1, L2
        end

        if L1 == '-' then
            if L2 == '0' then
                d = "-"
            end
            if L2 == '-' then
                d = "+-"
            end
        elseif L1 == '+' then
            if L2 == '0' then
                d = "+"
            elseif L2 == '-' then
                d = "0"
            elseif L2 == '+' then
                d = "-+"
            end
        elseif L1 == '0' then
            if L2 == '0' then
                d = "0"
            end
        end

        local ob1 = add(c1,d:sub(2,2))
        local ob2 = add(ob1,c2)

        out = ob2 .. d:sub(1,1)
    elseif b1 ~= "" then
        out = b1
    elseif b2 ~= "" then
        out = b2
    else
        out = ""
    end

    return out
end

function unary_minus(b)
    local out = ""

    for i=1, b:len() do
        local c = b:sub(i,i)
        if c == '-' then
            out = out .. '+'
        elseif c == '+' then
            out = out .. '-'
        else
            out = out .. c
        end
    end

    return out
end

function subtract(b1,b2)
    return add(b1, unary_minus(b2))
end

function mult(b1,b2)
    local r = "0"
    local c1 = b1
    local c2 = b2:reverse()

    for i=1,c2:len() do
        local c = c2:sub(i,i)
        if c == '+' then
            r = add(r, c1)
        elseif c == '-' then
            r = subtract(r, c1)
        end
        c1 = c1 .. '0'
    end

    while r:sub(1,1) == '0' do
        r = r:sub(2)
    end

    return r
end

function main()
    local a = "+-0++0+"
    local b = to_bt(-436)
    local c = "+-++-"
    local d = mult(a, subtract(b, c))

    print(string.format("      a: %14s %10d", a, from_bt(a)))
    print(string.format("      b: %14s %10d", b, from_bt(b)))
    print(string.format("      c: %14s %10d", c, from_bt(c)))
    print(string.format("a*(b-c): %14s %10d", d, from_bt(d)))
end

main()


Barnsley fern

A Barnsley fern is a fractal named after British mathematician Michael Barnsley and can be created using an iterated function system (IFS).

;Task:
Create this fractal fern, using the following transformations:
* f1   (chosen 1% of the time)
         xn + 1 = 0
         yn + 1 = 0.16 yn

* f2   (chosen 85% of the time)
         xn + 1 = 0.85 xn + 0.04 yn
         yn + 1 = -0.04 xn + 0.85 yn + 1.6

* f3   (chosen 7% of the time)
         xn + 1 = 0.2 xn - 0.26 yn
         yn + 1 = 0.23 xn + 0.22 yn + 1.6

* f4   (chosen 7% of the time)
         xn + 1 = -0.15 xn + 0.28 yn
         yn + 1 = 0.26 xn + 0.24 yn + 0.44.

Starting position: x = 0, y = 0

Solution:
g = love.graphics
wid, hei = g.getWidth(), g.getHeight()

function choose( i, j )
  local r = math.random()
  if r < .01 then return 0, .16 * j
    elseif r < .07 then return .2 * i - .26 * j, .23 * i + .22 * j + 1.6
    elseif r < .14 then return -.15 * i + .28 * j, .26 * i + .24 * j + .44
    else return .85 * i + .04 * j, -.04 * i + .85 * j + 1.6
  end
end
function createFern( iterations )
  local hw, x, y, scale = wid / 2, 0, 0, 45
  local pts = {}
  for k = 1, iterations do
    pts[1] = { hw + x * scale, hei - 15 - y * scale,
               20 + math.random( 80 ),
               128 + math.random( 128 ),
               20 + math.random( 80 ), 150 }
    g.points( pts )
    x, y = choose( x, y )
  end
end
function love.load()
  math.randomseed( os.time() )
  canvas = g.newCanvas( wid, hei )
  g.setCanvas( canvas )
  createFern( 15e4 )
  g.setCanvas()
end
function love.draw()
  g.draw( canvas )
end


Base64 decode data

See [[Base64 encode data]].

Now write a program that takes the output of the [[Base64 encode data]] task as input and regenerate the original file.

When working on the VBA implementation I found several 'solutions' on the net, including one from the software maker himself, that showed output with incorrect padding. Obviously with incorrect padding in the output you can not decode correctly to the original file again.

Solution:
-- Start taken from https://stackoverflow.com/a/35303321
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -- You will need this for encoding/decoding

-- decoding
function dec(data)
    data = string.gsub(data, '[^'..b..'=]', '')
    return (data:gsub('.', function(x)
        if (x == '=') then return '' end
        local r,f='',(b:find(x)-1)
        for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
        return r;
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
        if (#x ~= 8) then return '' end
        local c=0
        for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
            return string.char(c)
    end))
end
-- end of copy

local data = "VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIHlvdSBuZWVkIGEgY29tcHV0ZXIuCiAgICAtLVBhdWwgUi5FaHJsaWNo"
print(data)
print()

local decoded = dec(data)
print(decoded)


Bell numbers

Bell or exponential numbers are enumerations of the number of different ways to partition a set that has exactly '''n''' elements. Each element of the sequence '''Bn''' is the number of partitions of a set of size '''n''' where order of the elements and order of the partitions are non-significant. E.G.: '''{a b}''' is the same as '''{b a}''' and '''{a} {b}''' is the same as '''{b} {a}'''.

;So:

:'''B0 = 1''' trivially. There is only one way to partition a set with zero elements. '''{ }'''

:'''B1 = 1''' There is only one way to partition a set with one element. '''{a}'''

:'''B2 = 2''' Two elements may be partitioned in two ways. '''{a} {b}, {a b}'''

:'''B3 = 5''' Three elements may be partitioned in five ways '''{a} {b} {c}, {a b} {c}, {a} {b c}, {a c} {b}, {a b c}'''

: and so on.

A simple way to find the Bell numbers is construct a '''Bell triangle''', also known as an '''Aitken's array''' or '''Peirce triangle''', and read off the numbers in the first column of each row. There are other generating algorithms though, and you are free to choose the best / most appropriate for your case.

;Task:

Write a routine (function, generator, whatever) to generate the Bell number sequence and call the routine to show here, on this page at least the '''first 15''' and (if your language supports big Integers) '''50th''' elements of the sequence.

If you ''do'' use the Bell triangle method to generate the numbers, also show the '''first ten rows''' of the Bell triangle.

;See also:

:* '''OEIS:A000110 Bell or exponential numbers'''
:* '''OEIS:A011971 Aitken's array'''

Solution:
-- Bell numbers in Lua
-- db 6/11/2020 (to replace missing original)

local function bellTriangle(n)
  local tri = { {1} }
  for i = 2, n do
    tri[i] = { tri[i-1][i-1] }
    for j = 2, i do
      tri[i][j] = tri[i][j-1] + tri[i-1][j-1]
    end
  end
  return tri
end

local N = 25 -- in lieu of 50, practical limit with double precision floats
local tri = bellTriangle(N)

print("First 15 and "..N.."th Bell numbers:")
for i = 1, 15 do
  print(i, tri[i][1])
end
print(N, tri[N][1])

print()

print("First 10 rows of Bell triangle:")
for i = 1, 10 do
  print("[ " .. table.concat(tri[i],", ") .. " ]")
end


Benford's law

{{Wikipedia|Benford's_law}}

'''Benford's law''', also called the '''first-digit law''', refers to the frequency distribution of digits in many (but not all) real-life sources of data.

In this distribution, the number 1 occurs as the first digit about 30% of the time, while larger numbers occur in that position less frequently: 9 as the first digit less than 5% of the time. This distribution of first digits is the same as the widths of gridlines on a logarithmic scale.

Benford's law also concerns the expected distribution for digits beyond the first, which approach a uniform distribution.

This result has been found to apply to a wide variety of data sets, including electricity bills, street addresses, stock prices, population numbers, death rates, lengths of rivers, physical and mathematical constants, and processes described by power laws (which are very common in nature). It tends to be most accurate when values are distributed across multiple orders of magnitude.

A set of numbers is said to satisfy Benford's law if the leading digit d  (d \in \{1, \ldots, 9\}) occurs with probability

:::: P(d) = \log_{10}(d+1)-\log_{10}(d) = \log_{10}\left(1+\frac{1}{d}\right)

For this task, write (a) routine(s) to calculate the distribution of first significant (non-zero) digits in a collection of numbers, then display the actual vs. expected distribution in the way most convenient for your language (table / graph / histogram / whatever).

Use the first 1000 numbers from the Fibonacci sequence as your data set. No need to show how the Fibonacci numbers are obtained.

You can generate them or load them from a file; whichever is easiest.

Display your actual vs expected distribution.

''For extra credit:'' Show the distribution for one other set of numbers from a page on Wikipedia. State which Wikipedia page it can be obtained from and what the set enumerates. Again, no need to display the actual list of numbers or the code to load them.

;See also:
* numberphile.com.
* A starting page on Wolfram Mathworld is {{Wolfram|Benfords|Law}}.

Solution:
actual = {}
expected = {}
for i = 1, 9 do
    actual[i] = 0
    expected[i] = math.log10(1 + 1 / i)
end

n = 0
file = io.open("fibs1000.txt", "r")
for line in file:lines() do
    digit = string.byte(line, 1) - 48
    actual[digit] = actual[digit] + 1
    n = n + 1
end
file:close()

print("digit   actual  expected")
for i = 1, 9 do
    print(i, actual[i] / n, expected[i])
end


Best shuffle

Shuffle the characters of a string in such a way that as many of the character values are in a different position as possible.

A shuffle that produces a randomized result among the best choices is to be preferred. A deterministic approach that produces the same sequence every time is acceptable as an alternative.

Display the result as follows:

 original string, shuffled string, (score)

The score gives the number of positions whose character value did ''not'' change.

;Example:
 tree, eetr, (0)

;Test cases:
 abracadabra
 seesaw
 elk
 grrrrrr
 up
 a

;Related tasks
*   [[Anagrams/Deranged anagrams]]
*   [[Permutations/Derangements]]

Solution:
math.randomseed(os.time())

local function shuffle(t)
  for i = #t, 2, -1 do
    local j = math.random(i)
    t[i], t[j] = t[j], t[i]
  end
end

local function bestshuffle(s, r)
  local order, shufl, count = {}, {}, 0
  for ch in s:gmatch(".") do order[#order+1], shufl[#shufl+1] = ch, ch end
  if r then shuffle(shufl) end
  for i = 1, #shufl do
    for j = 1, #shufl do
      if i ~= j and shufl[i] ~= order[j] and shufl[j] ~= order[i] then
        shufl[i], shufl[j] = shufl[j], shufl[i]
      end
    end
  end
  for i = 1, #shufl do
    if shufl[i] == order[i] then
      count = count + 1
    end
  end
  return table.concat(shufl), count
end

local words = { "abracadabra", "seesaw", "elk", "grrrrrr", "up", "a" }

local function test(r)
  print(r and "RANDOM:" or "DETERMINISTIC:")
  for _, word in ipairs(words) do
    local shufl, count = bestshuffle(word, r)
    print(string.format("%s, %s, (%d)", word, shufl, count))
  end
  print()
end

test(true)
test(false)


Bin given limits

Solution:
function binner(limits, data)
  local bins = setmetatable({}, {__index=function() return 0 end})
  local n, flr = #limits+1, math.floor
  for _, x in ipairs(data) do
    local lo, hi = 1, n
    while lo < hi do
      local mid = flr((lo + hi) / 2)
      if not limits[mid] or x < limits[mid] then hi=mid else lo=mid+1 end
    end
    bins[lo] = bins[lo] + 1
  end
  return bins
end

function printer(limits, bins)
  for i = 1, #limits+1 do
    print(string.format("[%3s,%3s) : %d", limits[i-1] or " -∞", limits[i] or " +∞", bins[i]))
  end
end

print("PART 1:")
limits = {23, 37, 43, 53, 67, 83}
data = {95,21,94,12,99,4,70,75,83,93,52,80,57,5,53,86,65,17,92,83,71,61,54,58,47,
        16, 8, 9,32,84,7,87,46,19,30,37,96,6,98,40,79,97,45,64,60,29,49,36,43,55}
bins = binner(limits, data)
printer(limits, bins)

print("\nPART 2:")
limits = {14, 18, 249, 312, 389, 392, 513, 591, 634, 720}
data = {445,814,519,697,700,130,255,889,481,122,932, 77,323,525,570,219,367,523,442,933,
        416,589,930,373,202,253,775, 47,731,685,293,126,133,450,545,100,741,583,763,306,
        655,267,248,477,549,238, 62,678, 98,534,622,907,406,714,184,391,913, 42,560,247,
        346,860, 56,138,546, 38,985,948, 58,213,799,319,390,634,458,945,733,507,916,123,
        345,110,720,917,313,845,426,  9,457,628,410,723,354,895,881,953,677,137,397, 97,
        854,740, 83,216,421, 94,517,479,292,963,376,981,480, 39,257,272,157,  5,316,395,
        787,942,456,242,759,898,576, 67,298,425,894,435,831,241,989,614,987,770,384,692,
        698,765,331,487,251,600,879,342,982,527,736,795,585, 40, 54,901,408,359,577,237,
        605,847,353,968,832,205,838,427,876,959,686,646,835,127,621,892,443,198,988,791,
        466, 23,707,467, 33,670,921,180,991,396,160,436,717,918,  8,374,101,684,727,749}
bins = binner(limits, data)
printer(limits, bins)


Bioinformatics/Sequence mutation

Given a string of characters A, C, G, and T representing a DNA sequence write a routine to mutate the sequence, (string) by:
# Choosing a random base position in the sequence.
# Mutate the sequence by  doing one of either:
## '''S'''wap the base at that position by changing it to one of A, C, G, or T. (which has a chance of swapping the base for the same base)
## '''D'''elete the chosen base at the position.
## '''I'''nsert another base randomly chosen from A,C, G, or T into the sequence at that position.
# Randomly generate a test DNA sequence of at least 200 bases
# "Pretty print" the sequence and a count of its size, and the count of each base in the sequence
# Mutate the sequence ten times.
# "Pretty print" the sequence after all mutations, and a count of its size, and the count of each base in the sequence.

;Extra credit:
* Give more information on the individual mutations applied.
* Allow mutations to be weighted and/or chosen.

Solution:
math.randomseed(os.time())
bases = {"A","C","T","G"}
function randbase() return bases[math.random(#bases)] end

function mutate(seq)
  local i,h = math.random(#seq), "%-6s %3s at %3d"
  local old,new = seq:sub(i,i), randbase()
  local ops = {
    function(s) h=h:format("Swap", old..">"..new, i) return s:sub(1,i-1)..new..s:sub(i+1) end,
    function(s) h=h:format("Delete", " -"..old, i) return s:sub(1,i-1)..s:sub(i+1) end,
    function(s) h=h:format("Insert", " +"..new, i) return s:sub(1,i-1)..new..s:sub(i) end,
  }
  local weighted = { 1,1,2,3 }
  local n = weighted[math.random(#weighted)]
  return ops[n](seq), h
end

local seq,hist="",{} for i = 1, 200 do seq=seq..randbase() end
print("ORIGINAL:")
prettyprint(seq)
print()

for i = 1, 10 do seq,h=mutate(seq) hist[#hist+1]=h end
print("MUTATIONS:")
for i,h in ipairs(hist) do print("  "..h) end
print()

print("MUTATED:")
prettyprint(seq)


Bioinformatics/base count

Given this string representing ordered DNA bases:

CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT

;Task:
:*   "Pretty print" the sequence followed by a summary of the counts of each of the bases:   ('''A''', '''C''', '''G''', and '''T''')   in the sequence
:*   print the total count of each base in the string.

Solution:
function prettyprint(seq) -- approx DDBJ format
  seq = seq:gsub("%A",""):lower()
  local sums, n = { a=0, c=0, g=0, t=0 }, 1
  seq:gsub("(%a)", function(c) sums[c]=sums[c]+1 end)
  local function printf(s,...) io.write(s:format(...)) end
  printf("LOCUS       AB000000     %12d bp    mRNA    linear   HUM 01-JAN-2001\n", #seq)
  printf(" BASE COUNT %12d a %12d c %12d g %12d t\n", sums.a, sums.c, sums.g, sums.t)
  printf("ORIGIN\n")
  while n < #seq do
    local sub60 = seq:sub(n,n+59)
    printf("%9d %s\n", n, sub60:gsub("(..........)","%1 "))
    n = n + #sub60
  end
end

prettyprint[[
CGTAAAAAATTACAACGTCCTTTGGCTATCTCTTAAACTCCTGCTAAATG
CTCGTGCTTTCCAATTATGTAAGCGTTCCGAGACGGGGTGGTCGATTCTG
AGGACAAAGGTCAAGATGGAGCGCATCGAACGCAATAAGGATCATTTGAT
GGGACGTTTCGTCGACAAAGTCTTGTTTCGAGAGTAACGGCTACCGTCTT
CGATTCTGCTTATAACACTATGTTCTTATGAAATGGATGTTCTGAGTTGG
TCAGTCCCAATGTGCGGGGTTTCTTTTAGTACGTCGGGAGTGGTATTATA
TTTAATTTTTCTATATAGCGATCTGTATTTAAGCAATTCATTTAGGTTAT
CGCCGCGATGCTCGGTTCGGACCGCCAAGCATCTGGCTCCACTGCTAGTG
TCCTAAATTTGAATGGCAAACACAAATAAGATTTAGCAATTCGTGTAGAC
GACCGGGGACTTGCATGATGGGAGCAGCTTTGTTAAACTACGAACGTAAT
]]


Biorhythms

For a while in the late 70s, the pseudoscience of biorhythms was popular enough to rival astrology, with kiosks in malls that would give you your weekly printout. It was also a popular entry in "Things to Do with your Pocket Calculator" lists. You can read up on the history at Wikipedia, but the main takeaway is that unlike astrology, the math behind biorhythms is dead simple.

It's based on the number of days since your birth. The premise is that three cycles of unspecified provenance govern certain aspects of everyone's lives - specifically, how they're feeling physically, emotionally, and mentally. The best part is that not only do these cycles somehow have the same respective lengths for all humans of any age, gender, weight, genetic background, etc, but those lengths are an exact number of days. And the pattern is in each case a perfect sine curve. Absolutely miraculous!

To compute your biorhythmic profile for a given day, the first thing you need is the number of days between that day and your birth, so the answers in [[Days between dates]] are probably a good starting point. (Strictly speaking, the biorhythms start at 0 at the moment of your birth, so if you know time of day you can narrow things down further, but in general these operate at whole-day granularity.) Then take the residue of that day count modulo each of the the cycle lengths to calculate where the day falls on each of the three sinusoidal journeys.

The three cycles and their lengths are as follows:
:::{| class="wikitable"
! Cycle
! Length
|-
|Physical
| 23 days
|-
|Emotional
| 28 days
|-
|Mental
| 33 days
|}

The first half of each cycle is in "plus" territory, with a peak at the quarter-way point; the second half in "minus" territory, with a valley at the three-quarters mark. You can calculate a specific value between -1 and +1 for the ''k''th day of an ''n''-day cycle by computing '''sin( 2p''k'' / ''n'' )'''. The days where a cycle crosses the axis in either direction are called "critical" days, although with a cycle value of 0 they're also said to be the most neutral, which seems contradictory.

The task: write a subroutine, function, or program that will, given a birthdate and a target date, output the three biorhythmic values for the day. You may optionally include a text description of the position and the trend (e.g. "up and rising", "peak", "up but falling", "critical", "down and falling", "valley", "down but rising"), an indication of the date on which the next notable event (peak, valley, or crossing) falls, or even a graph of the cycles around the target date. Demonstrate the functionality for dates of your choice.

Example run of my Raku implementation:
raku br.raku 1943-03-09 1972-07-11

Solution:
cycles = {"Physical day ", "Emotional day", "Mental day   "}
lengths = {23, 28, 33}
quadrants = {
	{"up and rising", "peak"},
	{"up but falling", "transition"},
	{"down and falling", "valley"},
	{"down but rising", "transition"},
}

function parse_date_string (birthDate)
	local year, month, day = birthDate:match("(%d+)-(%d+)-(%d+)")
	return {year=tonumber(year), month=tonumber(month), day=tonumber(day)}
end

function days_diffeternce (d1, d2)
	if d1.year >= 1970 and d2.year >= 1970 then
		return math.floor(os.difftime(os.time(d2), os.time(d1))/(60*60*24))
	else
		local t1 = math.max (1970-d1.year, 1970-d2.year)
		t1 = math.ceil(t1/4)*4
		d1.year = d1.year + t1
		d2.year = d2.year + t1
		return math.floor(os.difftime(os.time(d2), os.time(d1))/(60*60*24))
	end
end

function biorhythms (birthDate, targetDate)
	local bd = parse_date_string(birthDate)
	local td = parse_date_string(targetDate)
	local days = days_diffeternce (bd, td)

	print('Born: '.. birthDate .. ', Target: ' .. targetDate)
	print("Day: ", days)
	for i=1, #lengths do
		local len = lengths[i]
		local posn = days%len
		local quadrant = math.floor(posn/len*4)+1
		local percent  = math.floor(math.sin(2*math.pi*posn/len)*1000)/10
		local cycle = cycles[i]
		local desc = percent > 95 and "peak" or
			percent < -95 and "valley" or
			math.abs(percent) < 5 and "critical transition" or "other"
		if desc == "other" then
			local t = math.floor(quadrant/4*len)-posn
			local qtrend, qnext = quadrants[quadrant][1], quadrants[quadrant][2]
			desc = percent .. '% (' .. qtrend .. ', next transition in ' .. t ..' days)'
		end
		print(cycle, posn..'/'..len, ': '.. desc)
	end
	print(' ')
end

datePairs = {
	{"1943-03-09", "1972-07-11"},
	{"1809-01-12", "1863-11-19"},
	{"1809-02-12", "1863-11-19"},
	{"2021-02-25", "2022-04-18"},
}

for i=1, #datePairs do
	biorhythms(datePairs[i][1], datePairs[i][2])
end


Bitwise IO

The aim of this task is to write functions (or create a class if yourlanguage is Object Oriented and you prefer) for reading and writing sequences of
bits, most significant bit first. While the output of a asciiprint "STRING" is the ASCII byte sequence
"S", "T", "R", "I", "N", "G", the output of a "print" of the bits sequence
0101011101010 (13 bits) must be 0101011101010; real I/O is performed always
''quantized'' by byte (avoiding endianness issues and relying on underlying
buffering for performance), therefore you must obtain as output the bytes
0101 0111 0101 0'''000''' (bold bits are padding bits), i.e. in hexadecimal 57 50.

As test, you can implement a '''rough''' (e.g. don't care about error handling or
other issues) compression/decompression program for ASCII sequences
of bytes, i.e. bytes for which the most significant bit is always unused, so that you can write
seven bits instead of eight (each 8 bytes of input, we write 7 bytes of output).

These bit oriented I/O functions can be used to implement compressors and
decompressors; e.g. Dynamic and Static Huffman encodings use variable length
bits sequences, while LZW (see [[LZW compression]]) use fixed or variable ''words''
nine (or more) bits long.

* Limits in the maximum number of bits that can be written/read in a single read/write operation are allowed.
* Errors handling is not mandatory

Solution:
local function BitWriter() return {
    accumulator = 0, -- For current byte.
    bitCount    = 0, -- Bits set in current byte.
    outChars    = {},

    -- writer:writeBit( bit )
    writeBit = function(writer, bit)
        writer.bitCount = writer.bitCount + 1
        if bit > 0 then
            writer.accumulator = writer.accumulator + 2^(8-writer.bitCount)
        end
        if writer.bitCount == 8 then
            writer:_flush()
        end
    end,

    -- writer:writeLsb( value, width )
    writeLsb = function(writer, value, width)
        for i = 1, width do
            writer:writeBit(value%2)
            value = math.floor(value/2)
        end
    end,

    -- dataString = writer:getOutput( )
    getOutput = function(writer)
        writer:_flush()
        return table.concat(writer.outChars)
    end,

    _flush = function(writer)
        if writer.bitCount == 0 then  return  end

        table.insert(writer.outChars, string.char(writer.accumulator))
        writer.accumulator = 0
        writer.bitCount    = 0
    end,
} end

local function BitReader(data) return {
    bitPosition = 0, -- Absolute position in 'data'.

    -- bit = reader:readBit( ) -- Returns nil at end-of-data.
    readBit = function(reader)
        reader.bitPosition = reader.bitPosition + 1
        local bytePosition = math.floor((reader.bitPosition-1)/8) + 1

        local byte = data:byte(bytePosition)
        if not byte then  return nil  end

        local bitIndex = ((reader.bitPosition-1)%8) + 1
        return math.floor(byte / 2^(8-bitIndex)) % 2
    end,

    -- value = reader:readLsb( width ) -- Returns nil at end-of-data.
    readLsb = function(reader, width)
        local accumulator = 0

        for i = 1, width do
            local bit = reader:readBit()
            if not bit then  return nil  end

            if bit > 0 then
                accumulator = accumulator + 2^(i-1)
            end
        end

        return accumulator
    end,
} end


Box the compass

Avast me hearties!
There be many a land lubber that knows naught of the pirate ways and gives direction by degree!
They know not how to box the compass!

;Task description:
# Create a function that takes a heading in degrees and returns the correct 32-point compass heading.
# Use the function to print and display a table of Index, Compass point, and Degree; rather like the corresponding columns from, the first table of the wikipedia article, but use only the following 33 headings as input:
:[0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5, 84.37, 84.38, 101.25, 118.12, 118.13, 135.0, 151.87, 151.88, 168.75, 185.62, 185.63, 202.5, 219.37, 219.38, 236.25, 253.12, 253.13, 270.0, 286.87, 286.88, 303.75, 320.62, 320.63, 337.5, 354.37, 354.38]. (They should give the same order of points but are spread throughout the ranges of acceptance).

;Notes;
* The headings and indices can be calculated from this pseudocode:
for i in 0..32 inclusive:
    heading = i * 11.25
    case i %3:
      if 1: heading += 5.62; break
      if 2: heading -= 5.62; break
    end
    index = ( i mod 32) + 1
* The column of indices can be thought of as an enumeration of the thirty two cardinal points (see talk page)..

Solution:
-- List of abbreviated compass point labels
compass_points = { "N", "NbE", "N-NE", "NEbN", "NE", "NEbE", "E-NE", "EbN",
                   "E", "EbS", "E-SE", "SEbE", "SE", "SEbS", "S-SE", "SbE",
                   "S", "SbW", "S-SW", "SWbS", "SW", "SWbW", "W-SW", "WbS",
                   "W", "WbN", "W-NW", "NWbW", "NW", "NWbN", "N-NW", "NbW" }

-- List of angles to test
test_angles = {  0.00,  16.87,  16.88,  33.75,  50.62,  50.63,  67.50,
                84.37,  84.38, 101.25, 118.12, 118.13, 135.00, 151.87,
               151.88, 168.75, 185.62, 185.63, 202.50, 219.37, 219.38,
               236.25, 253.12, 253.13, 270.00, 286.87, 286.88, 303.75,
               320.62, 320.63, 337.50, 354.37, 354.38 }

-- capitalize a string
function capitalize(s)
  return s:sub(1,1):upper() .. s:sub(2)
end

-- convert compass point abbreviation to full text of label
function expand_point(abbr)
  for from, to in pairs( { N="north", E="east", S="south", W="west",
                             b=" by " }) do
    abbr = abbr:gsub(from, to)
  end
  return capitalize(abbr)
end

-- modulus function that returns 1..N instead of 0..N-1
function adjusted_modulo(n, d)
  return 1 + (n - 1) % d
end

-- convert a compass angle from degrees into a box index (1..32)
function compass_point(degrees)
  degrees = degrees % 360
  return adjusted_modulo(1 + math.floor( (degrees+5.625) / 11.25), 32)
end

--  Now output the table of test data
header_format = "%-7s | %-18s | %s"
row_format = "%7.2f | %-18s | %2d"
print(header_format:format("Degrees", "Closest Point", "Index"))
for i, angle in ipairs(test_angles) do
  index = compass_point(angle)
  abbr  = compass_points[index]
  label  = expand_point(abbr)
  print(row_format:format(angle, label, index))
end


Brazilian numbers

Brazilian numbers are so called as they were first formally presented at the 1994 math Olympiad ''Olimpiada Iberoamericana de Matematica'' in Fortaleza, Brazil.

Brazilian numbers are defined as:

The set of positive integer numbers where each number '''N''' has at least one natural number '''B''' where '''1 < B < N-1''' where the representation of '''N''' in '''base B''' has all equal digits.

;E.G.:

:* '''1, 2 & 3''' can not be Brazilian; there is no base '''B''' that satisfies the condition '''1 < B < N-1'''.
:* '''4''' is not Brazilian; '''4''' in '''base 2''' is '''100'''. The digits are not all the same.
:* '''5''' is not Brazilian; '''5''' in '''base 2''' is '''101''', in '''base 3''' is '''12'''. There is no representation where the digits are the same.
:* '''6''' is not Brazilian; '''6''' in '''base 2''' is '''110''', in '''base 3''' is '''20''', in '''base 4''' is '''12'''. There is no representation where the digits are the same.
:* '''7''' ''is'' Brazilian; '''7''' in '''base 2''' is '''111'''. There is at least one representation where the digits are all the same.
:* '''8''' ''is'' Brazilian; '''8''' in '''base 3''' is '''22'''. There is at least one representation where the digits are all the same.
:* ''and so on...''

All even integers '''2P >= 8''' are Brazilian because '''2P = 2(P-1) + 2''', which is '''22''' in '''base P-1''' when '''P-1 > 2'''. That becomes true when '''P >= 4'''.
More common: for all all integers '''R''' and '''S''', where '''R > 1''' and also '''S-1 > R''', then '''R*S''' is Brazilian because '''R*S = R(S-1) + R''', which is '''RR''' in '''base S-1'''
The only problematic numbers are squares of primes, where R = S. Only 11^2 is brazilian to base 3.
All prime integers, that are brazilian, can only have the digit '''1'''. Otherwise one could factor out the digit, therefore it cannot be a prime number. Mostly in form of '''111''' to base Integer(sqrt(prime number)). Must be an odd count of '''1''' to stay odd like primes >  2

;Task:

Write a routine (function, whatever) to determine if a number is Brazilian and use the routine to show here, on this page;

:* the first '''20''' Brazilian numbers;
:* the first '''20 odd''' Brazilian numbers;
:* the first '''20 prime''' Brazilian numbers;

;See also:

:* '''OEIS:A125134 - Brazilian numbers'''
:* '''OEIS:A257521 - Odd Brazilian numbers'''
:* '''OEIS:A085104 - Prime Brazilian numbers'''

Solution:
function sameDigits(n,b)
    local f = n % b
    n = math.floor(n / b)
    while n > 0 do
        if n % b ~= f then
            return false
        end
        n = math.floor(n / b)
    end
    return true
end

function isBrazilian(n)
    if n < 7 then
        return false
    end
    if (n % 2 == 0) and (n >= 8) then
        return true
    end
    for b=2,n-2 do
        if sameDigits(n,b) then
            return true
        end
    end
    return false
end

function isPrime(n)
    if n < 2 then
        return false
    end
    if n % 2 == 0 then
        return n == 2
    end
    if n % 3 == 0 then
        return n == 3
    end

    local d = 5
    while d * d <= n do
        if n % d == 0 then
            return false
        end
        d = d + 2

        if n % d == 0 then
            return false
        end
        d = d + 4
    end

    return true
end

function main()
    local kinds = {" ", " odd ", " prime "}

    for i=1,3 do
        print("First 20" .. kinds[i] .. "Brazillion numbers:")
        local c = 0
        local n = 7
        while true do
            if isBrazilian(n) then
                io.write(n .. " ")
                c = c + 1
                if c == 20 then
                    print()
                    print()
                    break
                end
            end
            if i == 1 then
                n = n + 1
            elseif i == 2 then
                n = n + 2
            elseif i == 3 then
                repeat
                    n = n + 2
                until isPrime(n)
            end
        end
    end
end

main()


Break OO privacy

Show how to access private or protected members of a class in an object-oriented language from outside an instance of the class, without calling non-private or non-protected members of the class as a proxy.
The intent is to show how a debugger, serializer, or other meta-programming tool might access information that is barred by normal access methods to the object  but can nevertheless be accessed from within the language by some provided escape hatch or reflection mechanism.
The intent is specifically not to demonstrate heroic measures such as peeking and poking raw memory.

Note that cheating on your type system is almost universally regarded
as unidiomatic at best, and poor programming practice at worst.
Nonetheless, if your language intentionally maintains a double-standard for OO privacy, here's where you can show it off.

Solution:
local function Counter()
	-- These two variables are "private" to this function and can normally
	-- only be accessed from within this scope, including by any function
	-- created inside here.
	local counter = {}
	local count   = 0

	function counter:increment()
		-- 'count' is an upvalue here and can thus be accessed through the
		-- debug library, as long as we have a reference to this function.
		count = count + 1
	end

	return counter
end

-- Create a counter object and try to access the local 'count' variable.
local counter = Counter()

for i = 1, math.huge do
	local name, value = debug.getupvalue(counter.increment, i)
	if not name then  break  end -- No more upvalues.

	if name == "count" then
		print("Found 'count', which is "..tostring(value))
		-- If the 'counter.increment' function didn't access 'count'
		-- directly then we would never get here.
		break
	end
end


Burrows–Wheeler transform

{{Wikipedia|Burrows-Wheeler_transform}}

The Burrows-Wheeler transform (BWT, also called block-sorting compression) rearranges a character string into runs of similar characters.

This is useful for compression, since it tends to be easy to compress a string that has runs of repeated characters by techniques such as move-to-front transform and run-length encoding.

More importantly, the transformation is reversible, without needing to store any additional data.

The BWT is thus a "free" method of improving the efficiency of text compression algorithms, costing only some extra computation.

Source: Burrows-Wheeler transform

Solution:
STX = string.char(tonumber(2,16))
ETX = string.char(tonumber(3,16))

function bwt(s)
    if s:find(STX, 1, true) then
        error("String cannot contain STX")
    end
    if s:find(ETX, 1, true) then
        error("String cannot contain ETX")
    end

    local ss = STX .. s .. ETX
    local tbl = {}
    for i=1,#ss do
        local before = ss:sub(i + 1)
        local after = ss:sub(1, i)
        table.insert(tbl, before .. after)
    end

    table.sort(tbl)

    local sb = ""
    for _,v in pairs(tbl) do
        sb = sb .. string.sub(v, #v, #v)
    end

    return sb
end

function ibwt(r)
    local le = #r
    local tbl = {}

    for i=1,le do
        table.insert(tbl, "")
    end
    for j=1,le do
        for i=1,le do
            tbl[i] = r:sub(i,i) .. tbl[i]
        end
        table.sort(tbl)
    end

    for _,row in pairs(tbl) do
        if row:sub(le,le) == ETX then
            return row:sub(2, le - 1)
        end
    end

    return ""
end

function makePrintable(s)
    local a = s:gsub(STX, '^')
    local b = a:gsub(ETX, '|')
    return b
end

function main()
    local tests = {
        "banana",
        "appellee",
        "dogwood",
        "TO BE OR NOT TO BE OR WANT TO BE OR NOT?",
        "SIX.MIXED.PIXIES.SIFT.SIXTY.PIXIE.DUST.BOXES",
        STX .. "ABC" .. ETX
    }

    for _,test in pairs(tests) do
        print(makePrintable(test))
        io.write(" --> ")
        local t = ""
        if xpcall(
            function () t = bwt(test) end,
            function (err) print("ERROR: " .. err) end
        ) then
            print(makePrintable(t))
        end
        local r = ibwt(t)
        print(" --> " .. r)
        print()
    end
end

main()


CSV data manipulation

CSV spreadsheet files are suitable for storing tabular data in a relatively portable way.

The CSV format is flexible but somewhat ill-defined.

For present purposes, authors may assume that the data fields contain no commas, backslashes, or quotation marks.

;Task:
Read a CSV file, change some values and save the changes back to a file.

For this task we will use the following CSV file:

 C1,C2,C3,C4,C5
 1,5,9,13,17
 2,6,10,14,18
 3,7,11,15,19
 4,8,12,16,20

Suggestions

 Show how to add a column, headed 'SUM', of the sums of the rows.
 If possible, illustrate the use of built-in or standard functions, methods, or libraries, that handle generic CSV files.

Solution:
local csv={}
for line in io.lines('file.csv') do
    table.insert(csv, {})
    local i=1
    for j=1,#line do
        if line:sub(j,j) == ',' then
            table.insert(csv[#csv], line:sub(i,j-1))
            i=j+1
        end
    end
    table.insert(csv[#csv], line:sub(i,j))
end

table.insert(csv[1], 'SUM')
for i=2,#csv do
    local sum=0
    for j=1,#csv[i] do
        sum=sum + tonumber(csv[i][j])
    end
    if sum>0 then
        table.insert(csv[i], sum)
    end
end

local newFileData = ''
for i=1,#csv do
    newFileData=newFileData .. table.concat(csv[i], ',') .. '\n'
end

local file=io.open('file.csv', 'w')
file:write(newFileData)


CSV to HTML translation

Consider a simplified CSV format where all rows are separated by a newline
and all columns are separated by commas.

No commas are allowed as field data, but the data may contain
other characters and character sequences that would
normally be   ''escaped''   when converted to HTML

;Task:
Create a function that takes a string representation of the CSV data
and returns a text string of an HTML table representing the CSV data.

Use the following  data as the CSV text to convert, and show your output.
: Character,Speech
: The multitude,The messiah! Show us the messiah!
: Brians mother,Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!
: The multitude,Who are you?
: Brians mother,I'm his mother; that's who!
: The multitude,Behold his mother! Behold his mother!

;Extra credit:
''Optionally'' allow special formatting for the first row of the table as if it is the tables header row
(via  preferably; CSS if you must).

Solution:
FS = ","            -- field separator

csv = [[
Character,Speech
The multitude,The messiah! Show us the messiah!
Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitude,Who are you?
Brians mother,I'm his mother; that's who!
The multitude,Behold his mother! Behold his mother!
]]

csv = csv:gsub( "<", "<" )
csv = csv:gsub( ">", "&gr;" )

html = { "
" }
for line in string.gmatch( csv, "(.-\n)" ) do
    str = "
"
    for field in string.gmatch( line, "(.-)["..FS.."?\n?]" ) do
        str = str .. "" .. field .. "	"
    end
    str = str .. "
"
    html[#html+1] = str;
end
html[#html+1] = "
"

for _, line in pairs(html) do
    print(line)
end


Calculating the value of e

Calculate the value of   ''e''.

(''e''   is also known as   ''Euler's number''   and   ''Napier's constant''.)

See details: Calculating the value of e

Solution:
EPSILON = 1.0e-15;

fact = 1
e = 2.0
e0 = 0.0
n = 2

repeat
    e0 = e
    fact = fact * n
    n = n + 1
    e = e + 1.0 / fact
until (math.abs(e - e0) < EPSILON)

io.write(string.format("e = %.15f\n", e))


Call a function

Demonstrate the different syntax and semantics provided for calling a function.

This may include:
:*   Calling a function that requires no arguments
:*   Calling a function with a fixed number of arguments
:*   Calling a function with optional arguments
:*   Calling a function with a variable number of arguments
:*   Calling a function with named arguments
:*   Using a function in statement context
:*   Using a function in first-class context within an expression
:*   Obtaining the return value of a function
:*   Distinguishing built-in functions and user-defined functions
:*   Distinguishing subroutines and functions
;*   Stating whether arguments are passed by value or by reference
;*   Is partial application possible and how

This task is ''not'' about defining functions.

Solution:
-- Lua functions accept any number of arguments; missing arguments are nil-padded, extras are dropped.
function fixed (a, b, c) print(a, b, c) end
fixed() --> nil nil nil
fixed(1, 2, 3, 4, 5) --> 1 2 3

-- True vararg functions include a trailing ... parameter, which captures all additional arguments as a group of values.
function vararg (...) print(...) end
vararg(1, 2, 3, 4, 5) -- 1 2 3 4 5

-- Lua also allows dropping the parentheses if table or string literals are used as the sole argument
print "some string"
print { foo = "bar" } -- also serves as a form of named arguments

-- First-class functions in expression context
print(("this is backwards uppercase"):gsub("%w+", function (s) return s:upper():reverse() end))

-- Functions can return multiple values (including none), which can be counted via select()
local iter, obj, start = ipairs { 1, 2, 3 }
print(select("#", (function () end)())) --> 0
print(select("#", unpack { 1, 2, 3, 4 })) --> 4

-- Partial application
function prefix (pre)
    return function (suf) return pre .. suf end
end

local prefixed = prefix "foo"
print(prefixed "bar", prefixed "baz", prefixed "quux")

-- nil, booleans, and numbers are always passed by value. Everything else is always passed by reference.
-- There is no separate notion of subroutines
-- Built-in functions are not easily distinguishable from user-defined functions


Cantor set

Draw a Cantor set.

See details at this Wikipedia webpage:   Cantor set

Solution:
local WIDTH = 81
local HEIGHT = 5
local lines = {}

function cantor(start, length, index)
    -- must be local, or only one side will get calculated
    local seg = math.floor(length / 3)
    if 0 == seg then
        return nil
    end

    -- remove elements that are not in the set
    for it=0, HEIGHT - index do
        i = index + it
        for jt=0, seg - 1 do
            j = start + seg + jt
            pos = WIDTH * i + j
            lines[pos] = ' '
        end
    end

    -- left side
    cantor(start,           seg, index + 1)
    -- right side
    cantor(start + seg * 2, seg, index + 1)
    return nil
end

-- initialize the lines
for i=0, WIDTH * HEIGHT do
    lines[i] = '*'
end

-- calculate
cantor(0, WIDTH, 1)

-- print the result sets
for i=0, HEIGHT-1 do
    beg = WIDTH * i
    for j=beg, beg+WIDTH-1 do
        if j <= WIDTH * HEIGHT then
            io.write(lines[j])
        end
    end
    print()
end


Cartesian product of two or more lists

Show one or more idiomatic ways of generating the Cartesian product of two arbitrary lists in your language.

Demonstrate that your function/method correctly returns:
::{1, 2} x {3, 4} = {(1, 3), (1, 4), (2, 3), (2, 4)}

and, in contrast:
::{3, 4} x {1, 2} = {(3, 1), (3, 2), (4, 1), (4, 2)}

Also demonstrate, using your function/method, that the product of an empty list with any other list is empty.
:: {1, 2} x {} = {}
:: {} x {1, 2} = {}

For extra credit, show or write a function returning the n-ary product of an arbitrary number of lists, each of arbitrary length. Your function might, for example, accept a single argument which is itself a list of lists, and return the n-ary product of those lists.

Use your n-ary Cartesian product function to show the following products:
:: {1776, 1789} x {7, 12} x {4, 14, 23} x {0, 1}
:: {1, 2, 3} x {30} x {500, 100}
:: {1, 2, 3} x {} x {500, 100}

Solution:
local pk,upk = table.pack, table.unpack
  local getn = function(t)return #t end
  local const = function(k)return function(e) return k end end
  local function attachIdx(f)-- one-time-off function modifier
    local idx = 0
    return function(e)idx=idx+1 ; return f(e,idx)end
  end

  local function reduce(t,acc,f)
    for i=1,t.n or #t do acc=f(acc,t[i])end
    return acc
  end
  local function imap(t,f)
    local r = {n=t.n or #t, r=reduce, u=upk, m=imap}
    for i=1,r.n do r[i]=f(t[i])end
    return r
  end

  local function prod(...)
    local ts = pk(...)
    local limit = imap(ts,getn)
    local idx, cnt = imap(limit,const(1)),  0
    local max = reduce(limit,1,function(a,b)return a*b end)
    local function ret(t,i)return t[idx[i]] end
    return function()
      if cnt>=max then return end -- no more output
      if cnt==0 then -- skip for 1st
        cnt = cnt + 1
      else
        cnt, idx[#idx] = cnt + 1, idx[#idx] + 1
        for i=#idx,2,-1 do -- update index list
          if idx[i]<=limit[i] then
            break -- no further update need
          else -- propagate limit overflow
            idx[i],idx[i-1] = 1, idx[i-1]+1
          end
        end
      end
      return cnt,imap(ts,attachIdx(ret)):u()
    end
  end
--- test
  for i,a,b in prod({1,2},{3,4}) do
    print(i,a,b)
  end
  print()
  for i,a,b in prod({3,4},{1,2}) do
    print(i,a,b)
  end


Casting out nines

Task   (in three parts):

;Part 1
Write a procedure (say \mathit{co9}(x)) which implements Casting Out Nines as described by returning the checksum for x. Demonstrate the procedure using the examples given there, or others you may consider lucky.

Note that this function does nothing more than calculate the least positive residue, modulo 9. Many of the solutions omit Part 1 for this reason. Many languages have a modulo operator, of which this is a trivial application.

With that understanding, solutions to Part 1, if given, are encouraged to follow the naive pencil-and-paper or mental arithmetic of repeated digit addition understood to be "casting out nines", or some approach other than just reducing modulo 9 using a built-in operator. Solutions for part 2 and 3 are not required to make use of the function presented in part 1.

;Part 2
Notwithstanding past Intel microcode errors, checking computer calculations like this would not be sensible. To find a computer use for your procedure:
: Consider the statement "318682 is 101558 + 217124 and squared is 101558217124" (see: [[Kaprekar numbers#Casting Out Nines (fast)]]).
: note that 318682 has the same checksum as (101558 + 217124);
: note that 101558217124 has the same checksum as (101558 + 217124) because for a Kaprekar they are made up of the same digits (sometimes with extra zeroes);
: note that this implies that for Kaprekar numbers the checksum of k equals the checksum of k^2.

Demonstrate that your procedure can be used to generate or filter a range of numbers with the property \mathit{co9}(k) = \mathit{co9}(k^2) and show that this subset is a small proportion of the range and contains all the Kaprekar in the range.

;Part 3
Considering this MathWorld page, produce a efficient algorithm based on the more mathematical treatment of Casting Out Nines, and realizing:
: \mathit{co9}(x) is the residual of x mod 9;
: the procedure can be extended to bases other than 9.

Demonstrate your algorithm by generating or filtering a range of numbers with the property k%(\mathit{Base}-1) == (k^2)%(\mathit{Base}-1) and show that this subset is a small proportion of the range and contains all the Kaprekar in the range.

;related tasks
* [[First perfect square in base N with N unique digits]]
* [[Kaprekar numbers]]

Solution:
local N = 2
local base = 10
local c1 = 0
local c2 = 0

for k = 1, math.pow(base, N) - 1 do
    c1 = c1 + 1
    if k % (base - 1) == (k * k) % (base - 1) then
        c2 = c2 + 1
        io.write(k .. ' ')
    end
end

print()
print(string.format("Trying %d numbers instead of %d numbers saves %f%%", c2, c1, 100.0 - 100.0 * c2 / c1))


Catalan numbers/Pascal's triangle

Print out the first   '''15'''   Catalan numbers by extracting them from Pascal's triangle.

;See:
*   Catalan Numbers and the Pascal Triangle.      This method enables calculation of Catalan Numbers using only addition and subtraction.

*   Catalan's Triangle for a Number Triangle that generates Catalan Numbers using only addition.
*   Sequence A000108 on OEIS has a lot of information on Catalan Numbers.

;Related Tasks:
[[Pascal's triangle]]

Solution:
function nextrow (t)
    local ret = {}
    t[0], t[#t + 1] = 0, 0
    for i = 1, #t do ret[i] = t[i - 1] + t[i] end
    return ret
end

function catalans (n)
    local t, middle = {1}
    for i = 1, n do
        middle = math.ceil(#t / 2)
        io.write(t[middle] - (t[middle + 1] or 0) .. " ")
        t = nextrow(nextrow(t))
    end
end

catalans(15)


Catamorphism

''Reduce'' is a function or method that is used to take the values in an array or a list and apply a function to successive members of the list to produce (or reduce them to), a single value.

;Task:
Show how ''reduce'' (or ''foldl'' or ''foldr'' etc), work (or would be implemented) in your language.

;See also:
* Wikipedia article:   Fold
* Wikipedia article:   Catamorphism

Solution:
table.unpack = table.unpack or unpack -- 5.1 compatibility
local nums = {1,2,3,4,5,6,7,8,9}

function add(a,b)
   return a+b
end

function mult(a,b)
   return a*b
end

function cat(a,b)
   return tostring(a)..tostring(b)
end

local function reduce(fun,a,b,...)
   if ... then
      return reduce(fun,fun(a,b),...)
   else
      return fun(a,b)
   end
end

local arithmetic_sum = function (...) return reduce(add,...) end
local factorial5 = reduce(mult,5,4,3,2,1)

print("Σ(1..9)   : ",arithmetic_sum(table.unpack(nums)))
print("5!        : ",factorial5)
print("cat {1..9}: ",reduce(cat,table.unpack(nums)))


Chaocipher

Description:
The Chaocipher was invented by J.F.Byrne in 1918 and, although simple by modern cryptographic standards, does not appear to have been broken until the algorithm was finally disclosed by his family in 2010.

The algorithm is described in this paper by M.Rubin in 2010 and there is a C# implementation here.

;Task:
Code the algorithm in your language and to test that it works with the plaintext 'WELLDONEISBETTERTHANWELLSAID' used in the paper itself.

Solution:
-- Chaocipher, in Lua, 6/19/2020 db
local Chaocipher = {
  ct = "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
  pt = "PTLNBQDEOYSFAVZKGJRIHWXUMC",
  encrypt = function(self, text) return self:_encdec(text, true) end,
  decrypt = function(self, text) return self:_encdec(text, false) end,
  _encdec = function(self, text, encflag)
    local ct, pt, s = self.ct, self.pt, ""
    local cshl = function(s,i) return s:sub(i) .. s:sub(1,i-1) end
    local sshl = function(s,i) return s:sub(1,i-1) .. s:sub(i+1,14) .. s:sub(i,i) .. s:sub(15) end
    for ch in text:gmatch(".") do
      local i = (encflag and pt or ct):find(ch)
      s = s .. (encflag and ct or pt):sub(i,i)
      if encflag then print(ct, pt, ct:sub(i,i), pt:sub(i,i)) end
      ct, pt = sshl(cshl(ct, i), 2), sshl(cshl(pt, i+1), 3)
    end
    return s
  end,
}
local plainText = "WELLDONEISBETTERTHANWELLSAID"
local encryptText = Chaocipher:encrypt(plainText)
local decryptText = Chaocipher:decrypt(encryptText)
print()
print("The original text was:  " .. plainText)
print("The encrypted text is:  " .. encryptText)
print("The decrypted text is:  " .. decryptText)


Chaos game

The Chaos Game is a method of generating the attractor of an iterated function system (IFS).

One of the best-known and simplest examples creates a fractal, using a polygon and an initial point selected at random.

;Task
Play the Chaos Game using the corners of an equilateral triangle as the reference points.   Add a starting point at random (preferably inside the triangle).   Then add the next point halfway between the starting point and one of the reference points.   This reference point is chosen at random.

After a sufficient number of iterations, the image of a Sierpinski Triangle should emerge.

;See also
* The Game of Chaos

Solution:
math.randomseed( os.time() )
colors, orig = { { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 } }, {}

function love.load()
    wid, hei = love.graphics.getWidth(), love.graphics.getHeight()

    orig[1] = { wid / 2, 3 }
    orig[2] = { 3, hei - 3 }
    orig[3] = { wid - 3, hei - 3 }
    local w, h = math.random( 10, 40 ), math.random( 10, 40 )
    if math.random() < .5 then w = -w end
    if math.random() < .5 then h = -h end
    orig[4] = { wid / 2 + w, hei / 2 + h }

    canvas = love.graphics.newCanvas( wid, hei )
    love.graphics.setCanvas( canvas ); love.graphics.clear()
    love.graphics.setColor( 255, 255, 255 )
    love.graphics.points( orig )
    love.graphics.setCanvas()
end
function love.draw()
    local iter = 100 --> make this number bigger to speed up rendering
    for rp = 1, iter do
        local r, pts = math.random( 6 ), {}
        if r == 1 or r == 4 then
            pt = 1
        elseif r == 2 or r == 5 then
            pt = 2
        else
            pt = 3
        end
        local x, y = ( orig[4][1] + orig[pt][1] ) / 2, ( orig[4][2] + orig[pt][2] ) / 2
        orig[4][1] = x; orig[4][2] = y
        pts[1] = { x, y, colors[pt][1], colors[pt][2], colors[pt][3], 255 }
        love.graphics.setCanvas( canvas )
        love.graphics.points( pts )
    end
    love.graphics.setCanvas()
    love.graphics.draw( canvas )
end


Cheryl's birthday

Albert and Bernard just became friends with Cheryl, and they want to know when her birthday is.

Cheryl gave them a list of ten possible dates:
      May 15,     May 16,     May 19
      June 17,    June 18
      July 14,    July 16
      August 14,  August 15,  August 17

Cheryl then tells Albert the   ''month''   of birth,   and Bernard the   ''day''   (of the month)   of birth.
  1)  Albert:   I don't know when Cheryl's birthday is, but I know that Bernard does not know too.
  2)  Bernard:  At first I don't know when Cheryl's birthday is, but I know now.
  3)  Albert:   Then I also know when Cheryl's birthday is.

;Task
Write a computer program to deduce, by successive elimination, Cheryl's birthday.

;Related task:
* [[Sum and Product Puzzle]]

;References
* Wikipedia article of the same name.
* Tuple Relational Calculus

Solution:
-- Cheryl's Birthday in Lua 6/15/2020 db

local function Date(mon,day)
  return { mon=mon, day=day, valid=true }
end

local choices = {
  Date("May", 15), Date("May", 16), Date("May", 19),
  Date("June", 17), Date("June", 18),
  Date("July", 14), Date("July", 16),
  Date("August", 14), Date("August", 15), Date("August", 17)
}

local function apply(t, f)
  for k, v in ipairs(t) do
    f(k, v)
  end
end

local function filter(t, f)
  local result = {}
  for k, v in ipairs(t) do
    if f(k, v) then
      result[#result+1] = v
    end
  end
  return result
end

local function map(t, f)
  local result = {}
  for k, v in ipairs(t) do
    result[#result+1] = f(k, v)
  end
  return result
end

local function count(t) return #t end
local function isvalid(k, v) return v.valid end
local function invalidate(k, v) v.valid = false end
local function remaining() return filter(choices, isvalid) end

local function listValidChoices()
  print("   " .. table.concat(map(remaining(), function(k, v) return v.mon .. " " .. v.day end), ", "))
  print()
end

print("Cheryl offers these ten choices:")
listValidChoices()

print("1) Albert knows that Bernard also cannot yet know, so cannot be a month with a unique day, leaving:")
apply(remaining(), function(k, v)
  if count(filter(choices, function(k2, v2) return v.day==v2.day end)) == 1 then
    apply(filter(remaining(), function(k2, v2) return v.mon==v2.mon end), invalidate)
  end
end)
listValidChoices()

print("2) After Albert's revelation, Bernard now knows, so day must be unique, leaving:")
apply(remaining(), function(k, v)
  local subset = filter(remaining(), function(k2, v2) return v.day==v2.day end)
  if count(subset) > 1 then apply(subset, invalidate) end
end)
listValidChoices()

print("3) After Bernard's revelation, Albert now knows, so month must be unique, leaving only:")
apply(remaining(), function(k, v)
  local subset = filter(remaining(), function(k2, v2) return v.mon==v2.mon end)
  if count(subset) > 1 then apply(subset, invalidate) end
end)
listValidChoices()


Chinese remainder theorem

Suppose   n_1,   n_2,   \ldots,   n_k   are positive [[integer]]s that are pairwise co-prime.

Then, for any given sequence of integers   a_1,   a_2,   \dots,   a_k,   there exists an integer   x   solving the following system of simultaneous congruences:

::: \begin{align}
  x &\equiv a_1 \pmod{n_1} \\
  x &\equiv a_2 \pmod{n_2} \\
    &{}\  \  \vdots \\
  x &\equiv a_k \pmod{n_k}
\end{align}

Furthermore, all solutions   x   of this system are congruent modulo the product,    N=n_1n_2\ldots n_k.

;Task:
Write a program to solve a system of linear congruences by applying the   Chinese Remainder Theorem.

If the system of equations cannot be solved, your program must somehow indicate this.

(It may throw an exception or return a special false value.)

Since there are infinitely many solutions, the program should return the unique solution   s   where   0 \leq s \leq n_1n_2\ldots n_k.

''Show the functionality of this program'' by printing the result such that the   n's   are   [3,5,7]   and the   a's   are   [2,3,2].

'''Algorithm''':   The following algorithm only applies if the   n_i's   are pairwise co-prime.

Suppose, as above, that a solution is required for the system of congruences:

::: x \equiv a_i \pmod{n_i} \quad\mathrm{for}\; i = 1, \ldots, k

Again, to begin, the product   N = n_1n_2 \ldots n_k   is defined.

Then a solution   x   can be found as follows:

For each   i,   the integers   n_i   and   N/n_i   are co-prime.

Using the   Extended Euclidean algorithm,   we can find integers   r_i   and   s_i   such that   r_i n_i + s_i N/n_i = 1.

Then, one solution to the system of simultaneous congruences is:

::: x = \sum_{i=1}^k a_i s_i N/n_i

and the minimal solution,

::: x \pmod{N}.

Solution:
-- Taken from https://www.rosettacode.org/wiki/Sum_and_product_of_an_array#Lua
function prodf(a, ...) return a and a * prodf(...) or 1 end
function prodt(t) return prodf(unpack(t)) end

function mulInv(a, b)
    local b0 = b
    local x0 = 0
    local x1 = 1

    if b == 1 then
        return 1
    end

    while a > 1 do
        local q = math.floor(a / b)
        local amb = math.fmod(a, b)
        a = b
        b = amb
        local xqx = x1 - q * x0
        x1 = x0
        x0 = xqx
    end

    if x1 < 0 then
        x1 = x1 + b0
    end

    return x1
end

function chineseRemainder(n, a)
    local prod = prodt(n)

    local p
    local sm = 0
    for i=1,#n do
        p = prod / n[i]
        sm = sm + a[i] * mulInv(p, n[i]) * p
    end

    return math.fmod(sm, prod)
end

n = {3, 5, 7}
a = {2, 3, 2}
io.write(chineseRemainder(n, a))


Chinese zodiac

Determine the Chinese zodiac sign and related associations for a given year.
Traditionally, the Chinese have counted years using two lists of labels, one of length 10 (the "celestial stems") and one of length 12 (the "terrestrial branches"). The labels do not really have any meaning outside their positions in the two lists; they're simply a traditional enumeration device, used much as Westerners use letters and numbers. They were historically used for months and days as well as years, and the stems are still sometimes used for school grades.

Years cycle through both lists concurrently, so that both stem and branch advance each year; if we used Roman letters for the stems and numbers for the branches, consecutive years would be labeled A1, B2, C3, etc. Since the two lists are different lengths, they cycle back to their beginning at different points: after J10 we get A11, and then after B12 we get C1. The result is a repeating 60-year pattern within which each pair of names occurs only once.

Mapping the branches to twelve traditional animal deities results in the well-known "Chinese zodiac", assigning each year to a given animal. For example, Sunday, January 22, 2023 CE (in the common Gregorian calendar) began the lunisolar Year of the Rabbit.

The celestial stems do not have a one-to-one mapping like that of the branches to animals; however, the five pairs of consecutive stems are each associated with one of the five traditional Chinese elements (Wood, Fire, Earth, Metal, and Water). Further, one of the two years within each element is assigned to yin, the other to yang.

Thus, the Chinese year beginning in 2023 CE is also the yin year of Water. Since 12 is an even number, the association between animals and yin/yang doesn't change; consecutive Years of the Rabbit will cycle through the five elements, but will always be yin.

;Task: Create a subroutine or program that will return or output the animal, yin/yang association, and element for the lunisolar year that begins in a given CE year.

You may optionally provide more information in the form of the year's numerical position within the 60-year cycle and/or its actual Chinese stem-branch name (in Han characters or Pinyin transliteration).

;Requisite information:
* The animal cycle runs in this order: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey, Rooster, Dog, Pig.
* The element cycle runs in this order: Wood, Fire, Earth, Metal, Water.
* Each element gets two consecutive years; a yang followed by a yin.
* The current 60-year cycle began in 1984; any multiple of 60 years from that point may be used to reckon from.

Thus, year 1 of a cycle is the year of the Wood Rat (yang), year 2 the Wood Ox (yin), and year 3 the Fire Tiger (yang). The year 2023 - which, as already noted, is the year of the Water Rabbit (yin) - is the 40th year of the current cycle.

;Information for optional task:
* The ten celestial stems are '''Jia ''' ''jia'', '''Yi ''' ''yi'', '''Bing ''' ''bing'', '''Ding ''' ''ding'', '''Wu ''' ''wu'', '''Ji ''' ''ji'', '''Geng ''' ''geng'', '''Xin ''' ''xin'', '''Ren ''' ''ren'', and '''Gui ''' ''gui''. With the ASCII version of Pinyin tones, the names are written "jia3", "yi3", "bing3", "ding1", "wu4", "ji3", "geng1", "xin1", "ren2", and "gui3".
* The twelve terrestrial branches are '''Zi ''' ''zi'', '''Chou ''' ''chou'', '''Yin ''' ''yin'', '''Mao ''' ''mao'', '''Chen ''' ''chen'', '''Si ''' ''si'', '''Wu ''' ''wu'', '''Wei ''' ''wei'', '''Shen ''' ''shen'', '''You ''' ''you'', '''Xu ''' ''xu'', '''Hai ''' ''hai''. In ASCII Pinyin, those are "zi3", "chou3", "yin2", "mao3", "chen2", "si4", "wu3", "wei4", "shen1", "you3", "xu1", and "hai4".

Therefore 1984 was '''Jia Zi ''' (''jia-zi'', or jia3-zi3).  2023 is '''Gui Mao ''' (''gui-mao'' or gui3-mao3).

Solution:
local ANIMALS = {"Rat","Ox","Tiger","Rabbit","Dragon","Snake","Horse","Goat","Monkey","Rooster","Dog","Pig"}
local ELEMENTS = {"Wood","Fire","Earth","Metal","Water"}

function element(year)
    local idx = math.floor(((year - 4) % 10) / 2)
    return ELEMENTS[idx + 1]
end

function animal(year)
    local idx = (year - 4) % 12
    return ANIMALS[idx + 1]
end

function yy(year)
    if year % 2 == 0 then
        return "yang"
    else
        return "yin"
    end
end

function zodiac(year)
    local e = element(year)
    local a = animal(year)
    local y = yy(year)
    print(year.." is the year of the "..e.." "..a.." ("..y..")")
end

zodiac(1935)
zodiac(1938)
zodiac(1968)
zodiac(1972)
zodiac(1976)
zodiac(2017)


Church numerals

In the Church encoding of natural numbers, the number N is encoded by a function that applies its first argument N times to its second argument.

* '''Church zero''' always returns the identity function, regardless of its first argument. In other words, the first argument is not applied to the second argument at all.
* '''Church one''' applies its first argument f just once to its second argument x, yielding '''f(x)'''
* '''Church two''' applies its first argument f twice to its second argument x, yielding '''f(f(x))'''
* and each successive Church numeral applies its first argument one additional time to its second argument, '''f(f(f(x)))''', '''f(f(f(f(x))))''' ...  The Church numeral 4, for example, returns a quadruple composition of the function supplied as its first argument.

Arithmetic operations on natural numbers can be similarly represented as functions on Church numerals.

In your language define:

* Church Zero,
* a Church successor function (a function on a Church numeral which returns the next Church numeral in the series),
* functions for Addition, Multiplication and Exponentiation over Church numerals,
* a function to convert integers to corresponding Church numerals,
* and a function to convert Church numerals to corresponding integers.

You should:

* Derive Church numerals three and four in terms of Church zero and a Church successor function.
* use Church numeral arithmetic to obtain the the sum and the product of Church 3 and Church 4,
* similarly obtain 4^3 and 3^4 in terms of Church numerals, using a Church numeral exponentiation function,
* convert each result back to an integer, and return it or print it to the console.

Solution:
function churchZero()
  return function(x) return x end
end

function churchSucc(c)
  return function(f)
    return function(x)
      return f(c(f)(x))
    end
  end
end

function churchAdd(c, d)
  return function(f)
    return function(x)
      return c(f)(d(f)(x))
    end
  end
end

function churchMul(c, d)
  return function(f)
      return c(d(f))
  end
end

function churchExp(c, e)
  return e(c)
end

function numToChurch(n)
  local ret = churchZero
  for i = 1, n do
    ret = succ(ret)
  end
  return ret
end

function churchToNum(c)
  return c(function(x) return x + 1 end)(0)
end

three =  churchSucc(churchSucc(churchSucc(churchZero)))
four = churchSucc(churchSucc(churchSucc(churchSucc(churchZero))))

print("'three'\t=", churchToNum(three))
print("'four' \t=", churchToNum(four))
print("'three' * 'four' =", churchToNum(churchMul(three, four)))
print("'three' + 'four' =", churchToNum(churchAdd(three, four)))
print("'three' ^ 'four' =", churchToNum(churchExp(three, four)))
print("'four' ^ 'three' =", churchToNum(churchExp(four, three)))


Circles of given radius through two points

2 circles with a given radius through 2 points in 2D space.

Given two points on a plane and a radius, usually two circles of given radius can be drawn through the points.
;Exceptions:
# r==0.0 should be treated as never describing circles (except in the case where the points are coincident).
# If the points are coincident then an infinite number of circles with the point on their circumference can be drawn, unless r==0.0 as well which then collapses the circles to a point.
# If the points form a diameter then return two identical circles ''or'' return a single circle, according to which is the most natural mechanism for the implementation language.
# If the points are too far apart then no circles can be drawn.

;Task detail:
* Write a function/subroutine/method/... that takes two points and a radius and returns the two circles through those points, ''or some indication of special cases where two, possibly equal, circles cannot be returned''.
* Show here the output for the following inputs:

      p1                p2           r
0.1234, 0.9876    0.8765, 0.2345    2.0
0.0000, 2.0000    0.0000, 0.0000    1.0
0.1234, 0.9876    0.1234, 0.9876    2.0
0.1234, 0.9876    0.8765, 0.2345    0.5
0.1234, 0.9876    0.1234, 0.9876    0.0

;Related task:
*   [[Total circles area]].

;See also:
*   Finding the Center of a Circle from 2 Points and Radius from Math forum @ Drexel

Solution:
function distance(p1, p2)
    local dx = (p1.x-p2.x)
    local dy = (p1.y-p2.y)
    return math.sqrt(dx*dx + dy*dy)
end

function findCircles(p1, p2, radius)
    local seperation = distance(p1, p2)
    if seperation == 0.0 then
        if radius == 0.0 then
            print("No circles can be drawn through ("..p1.x..", "..p1.y..")")
        else
            print("Infinitely many circles can be drawn through ("..p1.x..", "..p1.y..")")
        end
    elseif seperation == 2*radius then
        local cx = (p1.x+p2.x)/2
        local cy = (p1.y+p2.y)/2
        print("Given points are opposite ends of a diameter of the circle with center ("..cx..", "..cy..") and radius "..radius)
    elseif seperation > 2*radius then
        print("Given points are further away from each other than a diameter of a circle with radius "..radius)
    else
        local mirrorDistance = math.sqrt(math.pow(radius,2) - math.pow(seperation/2,2))
        local dx = p2.x - p1.x
        local dy = p1.y - p2.y
        local ax = (p1.x + p2.x) / 2
        local ay = (p1.y + p2.y) / 2
        local mx = mirrorDistance * dx / seperation
        local my = mirrorDistance * dy / seperation
        c1 = {x=ax+my, y=ay+mx}
        c2 = {x=ax-my, y=ay-mx}

        print("Two circles are possible.")
        print("Circle C1 with center ("..c1.x..", "..c1.y.."), radius "..radius)
        print("Circle C2 with center ("..c2.x..", "..c2.y.."), radius "..radius)
    end
    print()
end

cases = {
    {x=0.1234, y=0.9876},   {x=0.8765, y=0.2345},
    {x=0.0000, y=2.0000},   {x=0.0000, y=0.0000},
    {x=0.1234, y=0.9876},   {x=0.1234, y=0.9876},
    {x=0.1234, y=0.9876},   {x=0.8765, y=0.2345},
    {x=0.1234, y=0.9876},   {x=0.1234, y=0.9876}
}
radii = { 2.0, 1.0, 2.0, 0.5, 0.0 }
for i=1, #radii do
    print("Case "..i)
    findCircles(cases[i*2-1], cases[i*2], radii[i])
end


Cistercian numerals

Cistercian numerals were used across Europe by Cistercian monks during the Late Medieval Period as an alternative to Roman numerals. They were used to represent base 10 integers from '''0''' to '''9999'''.

;How they work
All Cistercian numerals begin with a vertical line segment, which by itself represents the number '''0'''. Then, glyphs representing the digits '''1''' through '''9''' are optionally added to the four quadrants surrounding the vertical line segment. These glyphs are drawn with vertical and horizontal symmetry about the initial line segment. Each quadrant corresponds to a digit place in the number:

:* The '''upper-right''' quadrant represents the '''ones''' place.
:* The '''upper-left''' quadrant represents the '''tens''' place.
:* The '''lower-right''' quadrant represents the '''hundreds''' place.
:* The '''lower-left''' quadrant represents the '''thousands''' place.

Please consult the following image for examples of Cistercian numerals showing each glyph: [https://upload.wikimedia.org/wikipedia/commons/6/67/Cistercian_digits_%28vertical%29.svg]

;Task

:* Write a function/procedure/routine to display any given Cistercian numeral. This could be done by drawing to the display, creating an image, or even as text (as long as it is a reasonable facsimile).

:* Use the routine to show the following Cistercian numerals:

::* 0
::* 1
::* 20
::* 300
::* 4000
::* 5555
::* 6789
::* And a number of your choice!

;Notes
Due to the inability to upload images to Rosetta Code as of this task's creation, showing output here on this page is not required. However, it is welcomed -- especially for text output.

;See also

:* '''Numberphile - The Forgotten Number System'''
:* '''dcode.fr - Online Cistercian numeral converter'''

Solution:
function initN()
    local n = {}
    for i=1,15 do
        n[i] = {}
        for j=1,11 do
            n[i][j] = " "
        end
        n[i][6] = "x"
    end
    return n
end

function horiz(n, c1, c2, r)
    for c=c1,c2 do
        n[r+1][c+1] = "x"
    end
end

function verti(n, r1, r2, c)
    for r=r1,r2 do
        n[r+1][c+1] = "x"
    end
end

function diagd(n, c1, c2, r)
    for c=c1,c2 do
        n[r+c-c1+1][c+1] = "x"
    end
end

function diagu(n, c1, c2, r)
    for c=c1,c2 do
        n[r-c+c1+1][c+1] = "x"
    end
end

function initDraw()
    local draw = {}

    draw[1] = function(n) horiz(n, 6, 10, 0) end
    draw[2] = function(n) horiz(n, 6, 10, 4) end
    draw[3] = function(n) diagd(n, 6, 10, 0) end
    draw[4] = function(n) diagu(n, 6, 10, 4) end
    draw[5] = function(n) draw[1](n) draw[4](n) end
    draw[6] = function(n) verti(n, 0, 4, 10) end
    draw[7] = function(n) draw[1](n) draw[6](n) end
    draw[8] = function(n) draw[2](n) draw[6](n) end
    draw[9] = function(n) draw[1](n) draw[8](n) end

    draw[10] = function(n) horiz(n, 0, 4, 0) end
    draw[20] = function(n) horiz(n, 0, 4, 4) end
    draw[30] = function(n) diagu(n, 0, 4, 4) end
    draw[40] = function(n) diagd(n, 0, 4, 0) end
    draw[50] = function(n) draw[10](n) draw[40](n) end
    draw[60] = function(n) verti(n, 0, 4, 0) end
    draw[70] = function(n) draw[10](n) draw[60](n) end
    draw[80] = function(n) draw[20](n) draw[60](n) end
    draw[90] = function(n) draw[10](n) draw[80](n) end

    draw[100] = function(n) horiz(n, 6, 10, 14) end
    draw[200] = function(n) horiz(n, 6, 10, 10) end
    draw[300] = function(n) diagu(n, 6, 10, 14) end
    draw[400] = function(n) diagd(n, 6, 10, 10) end
    draw[500] = function(n) draw[100](n) draw[400](n) end
    draw[600] = function(n) verti(n, 10, 14, 10) end
    draw[700] = function(n) draw[100](n) draw[600](n) end
    draw[800] = function(n) draw[200](n) draw[600](n) end
    draw[900] = function(n) draw[100](n) draw[800](n) end

    draw[1000] = function(n) horiz(n, 0, 4, 14) end
    draw[2000] = function(n) horiz(n, 0, 4, 10) end
    draw[3000] = function(n) diagd(n, 0, 4, 10) end
    draw[4000] = function(n) diagu(n, 0, 4, 14) end
    draw[5000] = function(n) draw[1000](n) draw[4000](n) end
    draw[6000] = function(n) verti(n, 10, 14, 0) end
    draw[7000] = function(n) draw[1000](n) draw[6000](n) end
    draw[8000] = function(n) draw[2000](n) draw[6000](n) end
    draw[9000] = function(n) draw[1000](n) draw[8000](n) end

    return draw
end

function printNumeral(n)
    for i,v in pairs(n) do
        for j,w in pairs(v) do
            io.write(w .. " ")
        end
        print()
    end
    print()
end

function main()
    local draw = initDraw()
    for i,number in pairs({0, 1, 20, 300, 4000, 5555, 6789, 9999}) do
        local n = initN()
        print(number..":")
        local thousands = math.floor(number / 1000)
        number = number % 1000
        local hundreds = math.floor(number / 100)
        number = number % 100
        local tens = math.floor(number / 10)
        local ones = number % 10
        if thousands > 0 then
            draw[thousands * 1000](n)
        end
        if hundreds > 0 then
            draw[hundreds * 100](n)
        end
        if tens > 0 then
            draw[tens * 10](n)
        end
        if ones > 0 then
            draw[ones](n)
        end
        printNumeral(n)
    end
end

main()


Closures/Value capture

Create a list of ten functions, in the simplest manner possible   (anonymous functions are encouraged),   such that the function at index   '' i ''    (you may choose to start    '' i ''    from either    '''0'''    or    '''1'''),     when run, should return the square of the index,   that is,    '' i '' 2.

Display the result of running any but the last function, to demonstrate that the function indeed remembers its value.

;Goal:
Demonstrate how to create a series of independent closures based on the same template but maintain separate copies of the variable closed over.

In imperative languages, one would generally use a loop with a mutable counter variable.

For each function to maintain the correct number, it has to capture the ''value'' of the variable at the time it was created, rather than just a reference to the variable, which would have a different value by the time the function was run.

See also: [[Multiple distinct objects]]

Solution:
funcs={}
for i=1,10 do
    table.insert(funcs, function() return i*i end)
end
funcs[2]()
funcs[3]()


Comma quibbling

Comma quibbling is a task originally set by Eric Lippert in his blog.

;Task:

Write a function to generate a string output which is the concatenation of input words from a list/sequence where:
# An input of no words produces the output string of just the two brace characters "{}".
# An input of just one word, e.g. ["ABC"], produces the output string of the word inside the two braces, e.g. "{ABC}".
# An input of two words, e.g. ["ABC", "DEF"], produces the output string of the two words inside the two braces with the words separated by the string " and ", e.g. "{ABC and DEF}".
# An input of three or more words, e.g. ["ABC", "DEF", "G", "H"], produces the output string of all but the last word separated by ", " with the last word separated by " and " and all within braces; e.g. "{ABC, DEF, G and H}".

Test your function with the following series of inputs showing your output here on this page:
* []                       # (No input words).
* ["ABC"]
* ["ABC", "DEF"]
* ["ABC", "DEF", "G", "H"]

Note: Assume words are non-empty strings of uppercase characters for this task.

Solution:
function quibble (strTab)
    local outString, join = "{"
    for strNum = 1, #strTab do
        if strNum == #strTab then
            join = ""
        elseif strNum == #strTab - 1 then
            join = " and "
        else
            join = ", "
        end
        outString = outString .. strTab[strNum] .. join
    end
    return outString .. '}'
end

local testCases = {
    {},
    {"ABC"},
    {"ABC", "DEF"},
    {"ABC", "DEF", "G", "H"}
}
for _, input in pairs(testCases) do print(quibble(input)) end


Command-line arguments

{{selection|Short Circuit|Console Program Basics}} Retrieve the list of command-line arguments given to the program. For programs that only print the arguments when run directly, see [[Scripted main]].
See also [[Program name]].

For parsing command line arguments intelligently, see [[Parsing command-line arguments]].

Example command line:

 myprogram -c "alpha beta" -h "gamma"

Solution:
print( "Program name:", arg[0] )

print "Arguments:"
for i = 1, #arg do
    print( i," ", arg[i] )
end


Compare a list of strings

Given a   list   of arbitrarily many strings, show how to:

*   test if they are all lexically '''equal'''
*   test if every string is lexically '''less than''' the one after it  ''(i.e. whether the list is in strict ascending order)''

Each of those two tests should result in a single true or false value, which could be used as the condition of an    if    statement or similar.

If the input list has less than two elements, the tests should always return true.

There is ''no'' need to provide a complete program and output.

Assume that the strings are already stored in an array/list/sequence/tuple variable (whatever is most idiomatic) with the name   strings,   and just show the expressions for performing those two tests on it (plus of course any includes and custom functions etc. that it needs),   with as little distractions as possible.

Try to write your solution in a way that does not modify the original list,   but if it does then please add a note to make that clear to readers.

If you need further guidance/clarification,   see [[#Perl]] and [[#Python]] for solutions that use implicit short-circuiting loops,   and [[#Raku]] for a solution that gets away with simply using a built-in language feature.

Solution:
function identical(t_str)
    _, fst = next(t_str)
    if fst then
        for _, i in pairs(t_str) do
            if i ~= fst then return false end
        end
    end
    return true
end

function ascending(t_str)
    prev = false
    for _, i in ipairs(t_str) do
        if prev and prev >= i then return false end
        prev = i
    end
    return true
end

function check(str)
    t_str = {}
    for i in string.gmatch(str, "[%a_]+") do
        table.insert(t_str, i)
    end
    str = str .. ": "
    if not identical(t_str) then str = str .. "not " end
    str = str .. "identical and "
    if not ascending(t_str) then str = str .. "not " end
    print(str .. "ascending.")
end

check("ayu dab dog gar panda tui yak")
check("oy oy oy oy oy oy oy oy oy oy")
check("somehow   somewhere  sometime")
check("Hoosiers")
check("AA,BB,CC")
check("AA,AA,AA")
check("AA,CC,BB")
check("AA,ACB,BB,CC")
check("single_element")


Compiler/lexical analyzer

The C and Python versions can be considered reference implementations.

;Related Tasks
* Syntax Analyzer task
* Code Generator task
* Virtual Machine Interpreter task
* AST Interpreter task

Solution:
-- module basic_token_finder
local M = {} -- only items added to M will be public (via 'return M' at end)
local table, string = table, string
local error, tonumber, select, assert = error, tonumber, select, assert

local token_name = require 'token_name'
_ENV = {}

function next_token(line, pos, line_num) -- match a token at line,pos
    local function m(pat)
        from, to, capture = line:find(pat, pos)
        if from then
            pos = to + 1
            return capture
        end
    end

    local function ptok(str)
        return {name=token_name[str]}
    end

    local function op2c()
        local text = m'^([<>=!]=)' or m'^(&&)' or m'^(||)'
        if text then return ptok(text) end
    end

    local function op1c_or_symbol()
        local char = m'^([%*/%%%+%-<>!=%(%){};,])'
        if char then return ptok(char) end
    end

    local function keyword_or_identifier()
        local text = m'^([%a_][%w_]*)'
        if text then
            local name = token_name[text]
            return name and {name=name} or {name='Identifier', value=text}
        end
    end

    local function integer()
        local text = m'^(%d+)%f[^%w_]'
        if text then return {name='Integer', value=tonumber(text)} end
    end

    local subst = {['\\\\'] = '\\', ['\\n'] = '\n'}

    local function qchar()
        local text = m"^'([^\\])'" or m"^'(\\[\\n])'"
        if text then
            local value = #text==1 and text:byte() or subst[text]:byte()
            return {name='Integer', value=value}
        end
    end

    local function qstr()
        local text = m'^"([^"\n]*\\?)"'
        if text then
            local value = text:gsub('()(\\.?)', function(at, esc)
                local replace = subst[esc]
                if replace then
                    return replace
                else
                    error{err='bad_escseq', line=line_num, column=pos+at-1}
                end
            end)
            return {name='String', value=value}
        end
    end

    local found = (op2c() or op1c_or_symbol() or
                   keyword_or_identifier() or integer() or qchar() or qstr())
    if found then
        return found, pos
    end
end

function find_commentrest(line, pos, line_num, socpos)
    local sfrom, sto = line:find('%*%/', pos)
    if sfrom then
        return socpos, sto
    else
        error{err='unfinished_comment', line=line_num, column=socpos}
    end
end

function find_comment(line, pos, line_num)
    local sfrom, sto = line:find('^%/%*', pos)
    if sfrom then
        local efrom, eto = find_commentrest(line, sto+1, line_num, sfrom)
        return sfrom, eto
    end
end

function find_morecomment(line, pos, line_num)
    assert(pos==1)
    return find_commentrest(line, pos, line_num, pos)
end

function find_whitespace(line, pos, line_num)
    local spos = pos
    repeat
        local eto = select(2, line:find('^%s+', pos))
        if not eto then
            eto = select(2, find_comment(line, pos, line_num))
        end
        if eto then pos = eto + 1 end
    until not eto
    return spos, pos - 1
end

function M.find_token(line, pos, line_num, in_comment)
    local spos = pos
    if in_comment then
        pos = 1 + select(2, find_morecomment(line, pos, line_num))
    end
    pos = 1 + select(2, find_whitespace(line, pos, line_num))
    if pos > #line then
        return nil
    else
        local token, nextpos = next_token(line, pos, line_num)
        if token then
            token.line, token.column = line_num, pos
            return token, nextpos
        else
            error{err='invalid_token', line=line_num, column=pos}
        end
    end
end

-- M._ENV = _ENV
return M


Continued fraction

A number may be represented as a continued fraction (see Mathworld for more information) as follows:
:a_0 + \cfrac{b_1}{a_1 + \cfrac{b_2}{a_2 + \cfrac{b_3}{a_3 + \ddots}}}

The task is to write a program which generates such a number and prints a real representation of it. The code should be tested by calculating and printing the square root of 2, Napier's Constant, and Pi, using the following coefficients:

For the square root of 2, use a_0 = 1 then a_N = 2. b_N is always 1.

:\sqrt{2} = 1 + \cfrac{1}{2 + \cfrac{1}{2 + \cfrac{1}{2 + \ddots}}}

For Napier's Constant, use a_0 = 2, then a_N = N. b_1 = 1 then b_N = N-1.

:e = 2 + \cfrac{1}{1 + \cfrac{1}{2 + \cfrac{2}{3 + \cfrac{3}{4 + \ddots}}}}

For Pi, use a_0 = 3 then a_N = 6. b_N = (2N-1)^2.

:\pi = 3 + \cfrac{1}{6 + \cfrac{9}{6 + \cfrac{25}{6 + \ddots}}}

;See also:
:*   [[Continued fraction/Arithmetic]] for tasks that do arithmetic over continued fractions.

Solution:
function calc(fa, fb, expansions)
    local a = 0.0
    local b = 0.0
    local r = 0.0
    local i = expansions
    while i > 0 do
        a = fa(i)
        b = fb(i)
        r = b / (a + r)
        i = i - 1
    end
    a = fa(0)
    return a + r
end

function sqrt2a(n)
    if n ~= 0 then
        return 2.0
    else
        return 1.0
    end
end

function sqrt2b(n)
    return 1.0
end

function napiera(n)
    if n ~= 0 then
        return n
    else
        return 2.0
    end
end

function napierb(n)
    if n > 1.0 then
        return n - 1.0
    else
        return 1.0
    end
end

function pia(n)
    if n ~= 0 then
        return 6.0
    else
        return 3.0
    end
end

function pib(n)
    local c = 2.0 * n - 1.0
    return c * c
end

function main()
    local sqrt2  = calc(sqrt2a,  sqrt2b,  1000)
    local napier = calc(napiera, napierb, 1000)
    local pi     = calc(pia,     pib,     1000)
    print(sqrt2)
    print(napier)
    print(pi)
end

main()


Convert decimal number to rational

The task is to write a program to transform a decimal number into a fraction in lowest terms.

It is not always possible to do this exactly. For instance, while rational numbers can be converted to decimal representation, some of them need an infinite number of digits to be represented exactly in decimal form. Namely, repeating decimals such as 1/3 = 0.333...

Because of this, the following fractions cannot be obtained (reliably) unless the language has some way of representing repeating decimals:
* 67 / 74 = 0.9(054) = 0.9054054...
* 14 / 27 = 0.(518)  = 0.518518...

Acceptable output:

* 0.9054054 - 4527027 / 5000000
* 0.518518  - 259259 / 500000

Finite decimals are of course no problem:

* 0.75      - 3 / 4

Solution:
for _,v in ipairs({ 0.9054054, 0.518518, 0.75, math.pi }) do
  local n, d, dmax, eps = 1, 1, 1e7, 1e-15
  while math.abs(n/d-v)>eps and d<dmax do d=d+1 n=math.floor(v*d) end
  print(string.format("%15.13f --> %d / %d", v, n, d))
end


Convert seconds to compound duration

Write a function or program which:
*   takes a positive integer representing a duration in seconds as input (e.g., 100), and
*   returns a string which shows the same duration decomposed into:
:::*   weeks,
:::*   days,
:::*   hours,
:::*   minutes,   and
:::*   seconds.

This is detailed below (e.g., "2 hr, 59 sec").

Demonstrate that it passes the following three test-cases:

'''''Test Cases'''''

:::::{| class="wikitable"
|-
! input number
! output string
|-
| 7259
| 2 hr, 59 sec
|-
| 86400
| 1 d
|-
| 6000000
| 9 wk, 6 d, 10 hr, 40 min
|}

'''''Details'''''

The following five units should be used:
:::::{| class="wikitable"
|-
! unit
! suffix used in output
! conversion
|-
| week
| wk
| 1 week = 7 days
|-
| day
| d
| 1 day = 24 hours
|-
| hour
| hr
| 1 hour = 60 minutes
|-
| minute
| min
| 1 minute = 60 seconds
|-
| second
| sec
|
|}

However, '''only''' include quantities with non-zero values in the output (e.g., return "1 d" and not "0 wk, 1 d, 0 hr, 0 min, 0 sec").

Give larger units precedence over smaller ones as much as possible (e.g., return 2 min, 10 sec and not 1 min, 70 sec or 130 sec)

Mimic the formatting shown in the test-cases (quantities sorted from largest unit to smallest and separated by comma+space; value and unit of each quantity separated by space).

Solution:
function duration (secs)
    local units, dur = {"wk", "d", "hr", "min"}, ""
    for i, v in ipairs({604800, 86400, 3600, 60}) do
        if secs >= v then
            dur = dur .. math.floor(secs / v) .. " " .. units[i] .. ", "
            secs = secs % v
        end
    end
    if secs == 0 then
        return dur:sub(1, -3)
    else
        return dur .. secs .. " sec"
    end
end

print(duration(7259))
print(duration(86400))
print(duration(6000000))


Count the coins

There are four types of common coins in   US   currency:
:::#   quarters   (25 cents)
:::#   dimes   (10 cents)
:::#   nickels   (5 cents),   and
:::#   pennies   (1 cent)

There are six ways to make change for 15 cents:
:::#   A dime and a nickel
:::#   A dime and 5 pennies
:::#   3 nickels
:::#   2 nickels and 5 pennies
:::#   A nickel and 10 pennies
:::#   15 pennies

;Task:
How many ways are there to make change for a dollar using these common coins?     (1 dollar = 100 cents).

;Optional:
Less common are dollar coins (100 cents);   and very rare are half dollars (50 cents).   With the addition of these two coins, how many ways are there to make change for $1000?

(Note:   the answer is larger than   232).

;References:
* an algorithm from the book ''Structure and Interpretation of Computer Programs''.
* an article in the algorithmist.
* Change-making problem on Wikipedia.

Solution:
function countSums (amount, values)
    local t = {}
    for i = 1, amount do t[i] = 0 end
    t[0] = 1
    for k, val in pairs(values) do
        for i = val, amount do t[i] = t[i] + t[i - val] end
    end
    return t[amount]
end

print(countSums(100, {1, 5, 10, 25}))
print(countSums(100000, {1, 5, 10, 25, 50, 100}))


Create an HTML table

Create an HTML table.
* The table body should have at least three rows of three columns.
* Each of these three columns should be labelled "X", "Y", and "Z".
* An extra column should be added at either the extreme left or the extreme right of the table that has no heading, but is filled with sequential row numbers.
* The rows of the "X", "Y", and "Z" columns should be filled with random or sequential integers having 4 digits or less.
* The numbers should be aligned in the same fashion for all columns.

Solution:
function htmlTable (data)
    local html = "
\n
\n	\n"
    for _, heading in pairs(data[1]) do
        html = html .. "" .. heading .. "	" .. "\n"
    end
    html = html .. "
\n"
    for row = 2, #data do
        html = html .. "
\n" .. row - 1 .. "	\n"
        for _, field in pairs(data[row]) do
            html = html .. "" .. field .. "	\n"
        end
        html = html .. "
\n"
    end
    return html .. "
"
end

local tableData = {
    {"X", "Y", "Z"},
    {"1", "2", "3"},
    {"4", "5", "6"},
    {"7", "8", "9"}
}

print(htmlTable(tableData))


Cullen and Woodall numbers

A Cullen number is a number of the form '''n x 2n + 1''' where '''n''' is a natural number.

A Woodall number is very similar. It is a number of the form '''n x 2n - 1''' where '''n''' is a natural number.

So for each '''n''' the associated Cullen number and Woodall number differ by 2.

''Woodall numbers are sometimes referred to as Riesel numbers or Cullen numbers of the second kind.''

'''Cullen primes''' are Cullen numbers that are prime. Similarly, '''Woodall primes''' are Woodall numbers that are prime.

It is common to list the Cullen and Woodall primes by the value of '''n''' rather than the full evaluated expression. They tend to get very large very quickly. For example, the third Cullen prime, '''n''' == 4713, has 1423 digits when evaluated.

;Task

* Write procedures to find Cullen numbers and Woodall numbers.

* Use those procedures to find and show here, on this page the first 20 of each.

;Stretch

* Find and show the first '''5''' '''Cullen primes''' in terms of '''n'''.

* Find and show the first '''12''' '''Woodall primes''' in terms of '''n'''.

;See also

* OEIS:A002064 - Cullen numbers: a(n) = n*2^n + 1

* OEIS:A003261 - Woodall (or Riesel) numbers: n*2^n - 1

* OEIS:A005849 - Indices of prime Cullen numbers: numbers k such that k*2^k + 1 is prime

* OEIS:A002234 - Numbers k such that the Woodall number k*2^k - 1 is prime

Solution:
function T(t) return setmetatable(t, {__index=table}) end
table.range = function(t,n) local s=T{} for i=1,n do s[i]=i end return s end
table.map = function(t,f) local s=T{} for i=1,#t do s[i]=f(t[i]) end return s end

function cullen(n) return (n<<n)+1 end
print("First 20 Cullen numbers:")
print(T{}:range(20):map(cullen):concat(" "))

function woodall(n) return (n<<n)-1 end
print("First 20 Woodall numbers:")
print(T{}:range(20):map(woodall):concat(" "))


Currency

Show how to represent currency in a simple example, using a data type that represent exact values of dollars and cents.

;Note:
The '''IEEE 754''' binary floating point representations of numbers like   '''2.86'''   and   '''.0765'''   are not exact.

For this example, data will be two items with prices in dollars and cents, a quantity for each, and a tax rate.

Use the values:
::* 4000000000000000 hamburgers at $5.50 each       (four quadrillion burgers)
::* 2 milkshakes at $2.86 each, and
::* a tax rate of 7.65%.

(That number of hamburgers is a 4 with 15 zeros after it.    The number is contrived to exclude naive task solutions using 64 bit floating point types.)

Compute and output (show results on this page):
::* the total price before tax
::* the tax
::* the total with tax

The tax value must be computed by rounding to the nearest whole cent and this exact value must be added to the total price before tax.

The output must show dollars and cents with a decimal point.

The three results displayed should be:
::* 22000000000000005.72
::* 1683000000000000.44
::* 23683000000000006.16

Dollar signs and thousands separators are optional.

Solution:
C = setmetatable(require("bc"), {__call=function(t,...) return t.new(...) end})
C.digits(6) -- enough for .nn * .nnnn ==> .nnnnnn, follow with trunc(2) to trim trailing zeroes

subtot = (C"4000000000000000" * C"5.50" + C"2" * C"2.86"):trunc(2) -- cosmetic trunc
tax = (subtot * C"0.0765" + C"0.005"):trunc(2) -- rounding trunc
total = (subtot + tax):trunc(2) -- cosmetic trunc

print(("Before tax:  %20s"):format(subtot:tostring()))
print(("Tax       :  %20s"):format(tax:tostring()))
print(("With tax  :  %20s"):format(total:tostring()))


Currying

{{Wikipedia|Currying}}

;Task:
Create a simple demonstrative example of Currying in a specific language.

Add any historic details as to how the feature made its way into the language.

Solution:
local curry do
  local call,env = function(fn,...)return fn(...)end
  local fmt,cat,rawset,rawget,floor = string.format,table.concat,rawset,rawget,math.floor
  local curryHelper = setmetatable({},{
    __call = function(me, n, m, ...)return me[n*256+m](...)end,
    __index = function(me,k)
      local n,m = floor(k / 256), k % 256
      local r,s = {},{} for i=1,m do r[i],s[i]='_'..i,'_'..i end s[1+#s]='...'
      r,s=cat(r,','),cat(s,',')
      s = n<m and fmt('CALL(%s)',r) or fmt('function(...)return ME(%d,%d+select("#",...),%s)end',n,m,s)
      local sc = fmt('local %s=... return %s',r,s)
      rawset(me,k,(loadstring or load)(sc,'_',nil,env) )
      return rawget(me,k)
    end})
  env = {CALL=call,ME=curryHelper,select=select}
  function curry(...)
    local pn,n,fn = select('#',...),...
    if pn==1 then n,fn = debug.getinfo(n, 'u'),n ; n = n and n.nparams end
    if type(n)~='number' or n~=floor(n)then return nil,'invalid curry'
    elseif n<=0 then return fn -- edge case
    else return curryHelper(n,1,fn)
    end
  end
end

-- test
function add(x,y)
   return x+y
end

local adder = curry(add) -- get params count from debug.getinfo
assert(adder(3)(4) == 3+4)
local add2 = adder(2)
assert(add2(3) == 2+3)
assert(add2(5) == 2+5)