Big commit, basic functionality + plotting.
This commit is contained in:
commit
d6b615c2c1
|
@ -0,0 +1,5 @@
|
|||
--Dependencies for displaying things.
|
||||
local backend = {}
|
||||
|
||||
|
||||
return backend
|
|
@ -0,0 +1,7 @@
|
|||
--Lazy enumerator of countable infinite set.
|
||||
--Pass a function closure for generating the set element-by-element, in order.
|
||||
--This function can return a "simple" value followed by an "exact" value
|
||||
--Returns a table including:
|
||||
-- an array with all elements generated so far (grows as needed upon access)
|
||||
local enum = { __index = function( t, n ) for i = #t + 1, n do t[i] = t.next() end return rawget(t, n) end }
|
||||
return function(nextElement) return setmetatable( { ["next"] = nextElement }, enum ) end
|
|
@ -0,0 +1,88 @@
|
|||
local enum = require 'enumeration'
|
||||
|
||||
local function CalkinWilf()
|
||||
local n, d = 1, 1
|
||||
return enum( function()
|
||||
repeat n, d = d, ( 2 * d * math.floor( n / d ) - n + d )
|
||||
until n < d
|
||||
return n / d
|
||||
end )
|
||||
end
|
||||
|
||||
local function Random()
|
||||
return enum( math.random )
|
||||
end
|
||||
|
||||
--Breadth first traversal of the open left subtree of the Stern-Brocot tree.
|
||||
--Formed by inserting the mediants of previous rationals in the tree.
|
||||
local function SternBrocot()
|
||||
|
||||
--Rational numbers.
|
||||
local R
|
||||
do
|
||||
local rationals = {}
|
||||
local rmt = {
|
||||
__lt = function(a, b) return a[1]/a[2] < b[1]/b[2] end,
|
||||
__eq = function(a, b) return a[1] == b[1] and a[2] == b[2] end,
|
||||
__pow = function(a, b) return R( a[1] + b[1], a[2] + b[2] ) end, --mediant
|
||||
__call = function(t) return t[1]/t[2] end,
|
||||
__tostring = function(t) return string.format( "%d / %d", t[1], t[2] ) end }
|
||||
|
||||
R = function( n, d )
|
||||
if not rationals[d] then rationals[d] = {} end
|
||||
local den = rationals[d]
|
||||
|
||||
if den[n] then return den[n] end
|
||||
|
||||
local r = setmetatable( { n , d }, rmt )
|
||||
den[n] = r
|
||||
return r
|
||||
end
|
||||
end
|
||||
|
||||
--Flat list of all entries traversed so far.
|
||||
local seq = { R(0, 1), R(1, 2), R(1, 1) }
|
||||
local idx = 0
|
||||
local len = 3
|
||||
|
||||
local function nextRational()
|
||||
--Return every other entry: these are the most recently added mediants.
|
||||
idx = idx + 2
|
||||
if idx < len then return seq[idx] end
|
||||
|
||||
--We have finished iterating over the current level of the tree.
|
||||
--Create a new level by adding all the new mediants.
|
||||
|
||||
local nSeq = {}
|
||||
for i = 1, len - 1 do
|
||||
nSeq[ 2 * i - 1 ] = seq[i]
|
||||
nSeq[ 2 * i ] = seq[i] ^ seq[i + 1]
|
||||
end
|
||||
nSeq[ 2 * len - 1 ] = seq[ len ]
|
||||
seq = nSeq
|
||||
len = #seq
|
||||
--print( "RATIONALS: NEW LEVEL", len, unpack( nSeq ) )
|
||||
idx = 0
|
||||
return nextRational()
|
||||
end
|
||||
|
||||
return enum( nextRational )
|
||||
end
|
||||
|
||||
--Enumeration of dyadic rationals in the open unit interval, ascending lexicographic.
|
||||
local function Dyad()
|
||||
local n, d = -1, 2
|
||||
return enum( function()
|
||||
n = n + 2
|
||||
if n > d then n, d = 1, d * 2 end
|
||||
return n / d
|
||||
end )
|
||||
end
|
||||
|
||||
return {
|
||||
Dyad = Dyad,
|
||||
SternBrocot = SternBrocot,
|
||||
CalkinWilf = CalkinWilf,
|
||||
Random = Random,
|
||||
QuadIrr = false,
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
local enumerations = require 'enumerations'
|
||||
local twintree = require 'twintree'
|
||||
local step = coroutine.wrap( twintree.buildIncremental )
|
||||
local a = enumerations.Dyad()
|
||||
local b = enumerations.Random()
|
||||
step( a, b )
|
||||
|
||||
--Use LOVE for plotting.
|
||||
local love = assert( love )
|
||||
|
||||
local canvas = love.graphics.newCanvas()
|
||||
local numMapped, pointList = 0
|
||||
|
||||
local function swap( i, j )
|
||||
b[i], b[j] = b[j], b[i]
|
||||
end
|
||||
|
||||
local function reset()
|
||||
print( "changing coroutine" )
|
||||
step = coroutine.wrap( twintree.buildIncremental )
|
||||
print( "swapping" )
|
||||
for i = 1, 10000 do
|
||||
swap( i, math.random( 1, 10000 ) )
|
||||
end
|
||||
|
||||
pointList = nil
|
||||
numMapped = 0
|
||||
print( "step" )
|
||||
step( a, b )
|
||||
end
|
||||
|
||||
local function new()
|
||||
pointList = step()
|
||||
numMapped = numMapped + 1
|
||||
end
|
||||
|
||||
local function paint()
|
||||
local type = type
|
||||
--Make sure the whole point list consists of numbers!
|
||||
--We use the __call metamethod in non-number elements to coerce to numbers.
|
||||
--TODO: make this more generic, maybe amenable to FFI types.
|
||||
for i = 1, #pointList do
|
||||
if type( pointList[i] ) ~= 'number' then pointList[i] = pointList[i]() end
|
||||
end
|
||||
|
||||
--print( "Points: " )
|
||||
--for i = 1, #pointList - 1, 2 do
|
||||
-- print(pointList[i], pointList[i + 1] )
|
||||
--end
|
||||
|
||||
love.graphics.setCanvas( canvas )
|
||||
love.graphics.clear()
|
||||
love.graphics.setColor( 1, 1, 1, 0.5 )
|
||||
love.graphics.print( numMapped )
|
||||
love.graphics.push()
|
||||
love.graphics.scale( love.graphics.getWidth(), love.graphics.getHeight() )
|
||||
love.graphics.translate( 0, 1 )
|
||||
love.graphics.scale( 1, -1 )
|
||||
love.graphics.points( pointList )
|
||||
love.graphics.line( pointList )
|
||||
love.graphics.pop()
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
|
||||
function love.wheelmoved()
|
||||
for i = 1, 3 do new() end
|
||||
paint()
|
||||
--[[ for i = 1, #sb do
|
||||
print( i, sb[i], dy[i] )
|
||||
end]]
|
||||
end
|
||||
|
||||
function love.mousepressed()
|
||||
--[[for i = 1, #sb do
|
||||
print( i, sb[i] )
|
||||
end]]
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
love.graphics.setCanvas()
|
||||
love.graphics.setColor( 1, 1, 1, 1 )
|
||||
love.graphics.draw( canvas )
|
||||
end
|
||||
|
||||
function love.keypressed()
|
||||
print( "pressed key!" )
|
||||
return reset() and love.wheelmoved()
|
||||
end
|
||||
|
||||
function love.load()
|
||||
love.graphics.setLineWidth( 0.001 )
|
||||
end
|
|
@ -0,0 +1,132 @@
|
|||
local function Gap(t, i)
|
||||
if not t[i] then return i else return Gap(t, i + 1) end
|
||||
end
|
||||
|
||||
local function Gaps(t, i)
|
||||
return Gap, t, i
|
||||
end
|
||||
|
||||
--Data structure supporting fast ordered insertion and search.
|
||||
local function Order()
|
||||
local o = {}
|
||||
|
||||
local list = { val = nil, prev = nil, next = nil }
|
||||
|
||||
o.Get = function( x )
|
||||
|
||||
end
|
||||
|
||||
o.GetAdjacent = function( node )
|
||||
return node.prev.val, node.next.val
|
||||
end
|
||||
|
||||
o.Insert = function( x )
|
||||
if not list.val then
|
||||
list.val = x
|
||||
return list
|
||||
end
|
||||
|
||||
local node = list
|
||||
while node and node.val < x do node = node.next end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
--Construct an isomorphism between two countable dense total orders
|
||||
--these orders are specified by functions which return the next element in some enumeration
|
||||
--these elements must be comparable with comparison operators <, >, &c.
|
||||
local function Isomorphism( Enumeration, InverseEnumeration, inv )
|
||||
|
||||
--Exported table.
|
||||
local t = {}
|
||||
local enum, order, mapping, inverse
|
||||
|
||||
--Another Isomorphism structure,
|
||||
--which contains the image of this isomorphism and the inverse
|
||||
--isomorphism mapping back to our dense countable linear order.
|
||||
inverse = inv or Isomorphism( InverseEnumeration, nil, t )
|
||||
|
||||
--Array for caching and lazily evaluating the supplied Enumeration,
|
||||
--an iterator that enumerates the elements of a dense countable linear order.
|
||||
enum = { Enumeration() }
|
||||
setmetatable(enum, {__index = function(_, i)
|
||||
for j = #enum + 1, i do
|
||||
enum[j] = Enumeration()
|
||||
end
|
||||
return rawget( enum, i )
|
||||
end})
|
||||
t.enum = enum --Expose as field
|
||||
|
||||
--Structure supporting fast ordered insertion and search.
|
||||
--Values are i such that mapping[order[i]] is not nil,
|
||||
--sorted according to getmetatable( enum[i] ).__lt
|
||||
order = { 1 }
|
||||
|
||||
--Find the indices i and k such that
|
||||
--enum[i] is the greatest lower bound on enum[j] in the mapping's support
|
||||
--enum[k] is the least upper bound on enum[j] in the mapping's support
|
||||
local function GetBounds( leaf )
|
||||
|
||||
end
|
||||
|
||||
--Array defining isomorphism mapping this dense countable order to the one in inverse.
|
||||
--If mapping[i] = j, then f(enum[i]) = inverse.enum[j]
|
||||
mapping = { 1 }
|
||||
setmetatable( mapping, {__newindex = function(map, k, v)
|
||||
rawset(mapping, k, v)
|
||||
|
||||
|
||||
end})
|
||||
|
||||
--Iterates over the natural numbers outside the isomorphism's support,
|
||||
--so returns the least n such that n >= i and mapping[n] == nil.
|
||||
--(Warning, infinite loop! Make sure to break out of this.)
|
||||
local function NextGap(i)
|
||||
if not mapping[i] then return i else return NextGap(i + 1) end --tail-recursive
|
||||
end
|
||||
|
||||
--Include another point in the mapping's support.
|
||||
--Infinite loop: ))<<>>((
|
||||
function t.ExpandMap()
|
||||
local gapIdx = NextGap(1)
|
||||
local pred, succ = GetBounds( OrderedInsertion( gapIdx ) )
|
||||
mapping[gapIdx] = inverse.AddNextBetween( i, mapping[pred], mapping[succ] )
|
||||
return inverse.ExpandMap()
|
||||
end
|
||||
|
||||
--Find the first j such that mapping[j] == nil,
|
||||
--enum[lower] < enum[j] < enum[upper]
|
||||
--Then map j to i, and add j to the order.
|
||||
function t.AddNextBetween( i, lower, upper )
|
||||
local p, q, r, j = enum[lower], nil, enum[upper], nil
|
||||
|
||||
for k in Gaps( 1 ) do
|
||||
q = enum[k]
|
||||
if ((not p) or (p < q))
|
||||
and ((not r) or (q < r)) then
|
||||
j = k
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
mapping[j] = i
|
||||
OrderedInsertion(j)
|
||||
return j
|
||||
end
|
||||
|
||||
--Get the isomorphism as a dictionary, for plotting.
|
||||
function t.GetMapping( )
|
||||
local map = {}
|
||||
for idx, imageIdx in pairs( mapping ) do
|
||||
map[ enum[ idx ] ] = inverse.enum[ imageIdx ]
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
return setmetatable( { Isomorphism = Isomorphism }, {__call = Isomorphism } )
|
|
@ -0,0 +1,25 @@
|
|||
--Partial sequence s:S->N, S <= N, N = { 1, 2, 3, ... }
|
||||
--Consists of:
|
||||
--A sequence stored as a (sparse) array
|
||||
--["cmin"], a key whose value is the smallest element outside the support of the sequence
|
||||
local sequence = {}
|
||||
sequence.__index = sequence
|
||||
function sequence.new( name )
|
||||
return setmetatable( { cmin = 1, name = name }, sequence )
|
||||
end
|
||||
|
||||
function sequence:insert( x, y )
|
||||
if self.cmin == x then
|
||||
repeat self.cmin = self.cmin + 1 until not self[self.cmin]
|
||||
end
|
||||
self[x] = y
|
||||
--debug:
|
||||
--print( "First unclaimed element:", self.name, self.cmin )
|
||||
end
|
||||
|
||||
--Maybe leave this for later, we'll inline it for now.
|
||||
function sequence:complement()
|
||||
|
||||
end
|
||||
|
||||
return sequence
|
|
@ -0,0 +1,103 @@
|
|||
local sequence = require 'sequence'
|
||||
|
||||
local maxLevels = 20 --Maximum depth of tree.
|
||||
local baseIdx = 1 --Base index of implicit tree. Is it a 0-based or 1-based array?
|
||||
local tree = {}
|
||||
|
||||
local function insert( aTree, aSeq, aVals, bTree, bSeq, bVals )
|
||||
|
||||
--print( "INSERTING" )
|
||||
local treeIdx = 1 --Pointer into implicit binary tree.
|
||||
local aIdx, bIdx = aSeq.cmin, bSeq.cmin --First elements of a and b outside the support of the sequence
|
||||
local aVal, bVal = aVals[aIdx], bVals[bIdx]
|
||||
local aTest, bTest
|
||||
|
||||
--Search for appropriate bIdx.
|
||||
--Sentinel value.
|
||||
local newCandidate = false
|
||||
while aTree[treeIdx] do
|
||||
if newCandidate then
|
||||
newCandidate = false
|
||||
treeIdx = 1
|
||||
repeat bIdx = bIdx + 1 until ( not bSeq[bIdx] )
|
||||
bVal = bVals[bIdx]
|
||||
end
|
||||
|
||||
--invariants guarantee these aren't empty:
|
||||
--loop invariant, that aTree[treeIdx] is nonempty
|
||||
--struct invariant, that bTree and aTree have identical support
|
||||
aTest, bTest = aTree[treeIdx], bTree[treeIdx]
|
||||
--Go left if bounded above, go right if bounded below,
|
||||
--if there is a discrepancy then start over with the next bIdx
|
||||
--print( "VALUES:", "A[[", aVal, aTest, "B[[", bVal, bTest )
|
||||
if ( aVal < aTest ) then
|
||||
if ( bVal < bTest ) then
|
||||
--print( "A LEFT B LEFT", aVal, aTest, bVal, bTest ) --debug
|
||||
treeIdx = 2 * treeIdx
|
||||
else
|
||||
--print( "A LEFT B RIGHT", aVal, aTest, bVal, bTest ) --debug
|
||||
newCandidate = true
|
||||
end
|
||||
elseif ( bVal > bTest ) then
|
||||
--print( "A RIGHT B RIGHT", aVal, aTest, bVal, bTest ) --debug
|
||||
treeIdx = 2 * treeIdx + 1
|
||||
else
|
||||
--print( "A RIGHT B LEFT", aVal, aTest, bVal, bTest ) --debug
|
||||
newCandidate = true
|
||||
end
|
||||
end
|
||||
|
||||
--Insert elements into tree.
|
||||
aTree[treeIdx], bTree[treeIdx] = aVal, bVal
|
||||
|
||||
--Insert indices into sequence.
|
||||
aSeq:insert( aIdx, bIdx )
|
||||
bSeq:insert( bIdx, aIdx )
|
||||
|
||||
end
|
||||
|
||||
--In-order traversal of the tree.
|
||||
--Returns points of twin-tree, sorted and interlaced, for graphing.
|
||||
-- { a_min, b_min, a_next, b_next, ...}
|
||||
do
|
||||
local a, b, n, result
|
||||
local function flat( idx )
|
||||
if a[ 2 * idx ] then flat( 2 * idx ) end
|
||||
result[ n ], result[ n + 1 ], n = a[idx], b[idx], n + 2
|
||||
if a[ 2 * idx + 1 ] then flat( 2 * idx + 1 ) end
|
||||
end
|
||||
|
||||
function tree.flat( aTree, bTree, t )
|
||||
result = t or {}
|
||||
a, b = aTree, bTree
|
||||
n = 1
|
||||
flat( 1 )
|
||||
for i = n + 1, #result do result[i] = nil end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
function tree.build( n, av, bv )
|
||||
local at, bt = {}, {}
|
||||
local as, bs = sequence.new 'a', sequence.new 'b'
|
||||
for i = 1, n do
|
||||
insert( at, as, av, bt, bs, bv ) --Forward map.
|
||||
insert( bt, bs, bv, at, as, av ) --Backward map.
|
||||
end
|
||||
return at, bt
|
||||
end
|
||||
|
||||
do
|
||||
local yield = assert( coroutine.yield )
|
||||
function tree.buildIncremental( av, bv )
|
||||
local at, bt = {}, {}
|
||||
local as, bs = sequence.new 'a', sequence.new 'b'
|
||||
local flattened = {}
|
||||
repeat
|
||||
insert( at, as, av, bt, bs, bv ) --Forward map.
|
||||
insert( bt, bs, bv, at, as, av ) --Backward map.
|
||||
until yield( tree.flat( at, bt, flattened ) ) and false
|
||||
end
|
||||
end
|
||||
|
||||
return tree
|
Loading…
Reference in New Issue