Add LIS opt
This commit is contained in:
parent
1b56477e59
commit
ae448f947f
2 changed files with 208 additions and 152 deletions
326
src/karax.nim
326
src/karax.nim
|
|
@ -1,6 +1,6 @@
|
||||||
## Karax -- Single page applications for Nim.
|
## Karax -- Single page applications for Nim.
|
||||||
|
|
||||||
import dom, vdom, jstrutils, components, jdict
|
import dom, vdom, jstrutils, components, jdict, tables
|
||||||
|
|
||||||
export dom.Event
|
export dom.Event
|
||||||
|
|
||||||
|
|
@ -123,28 +123,20 @@ proc replaceById(id: cstring; newTree: Node) =
|
||||||
#newTree.id = id
|
#newTree.id = id
|
||||||
|
|
||||||
proc equalsShallow(a, b: VNode): bool =
|
proc equalsShallow(a, b: VNode): bool =
|
||||||
if a.kind != b.kind: return false
|
if a.key == -1 and b.key == -1:
|
||||||
if a.id != b.id: return false
|
return eq(a, b)
|
||||||
if a.key != b.key: return false
|
|
||||||
if a.kind == VNodeKind.text:
|
|
||||||
if a.text != b.text: return false
|
|
||||||
elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk:
|
|
||||||
if a.text != b.text: return false
|
|
||||||
if not sameAttrs(a, b): return false
|
|
||||||
if a.class != b.class: return false
|
|
||||||
# XXX test event listeners here?
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc equalsTree(a, b: VNode): bool =
|
|
||||||
when false:
|
|
||||||
# hashing is too fragile now with component support:
|
|
||||||
if not a.validHash:
|
|
||||||
a.calcHash()
|
|
||||||
if not b.validHash:
|
|
||||||
b.calcHash()
|
|
||||||
return a.hash == b.hash
|
|
||||||
else:
|
else:
|
||||||
result = eq(a, b)
|
if a.kind != b.kind: return false
|
||||||
|
if a.id != b.id: return false
|
||||||
|
if a.key != b.key: return false
|
||||||
|
if a.kind == VNodeKind.text:
|
||||||
|
if a.text != b.text: return false
|
||||||
|
elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk:
|
||||||
|
if a.text != b.text: return false
|
||||||
|
if not sameAttrs(a, b): return false
|
||||||
|
if a.class != b.class: return false
|
||||||
|
# XXX test event listeners here?
|
||||||
|
return true
|
||||||
|
|
||||||
proc updateDirtyElements(parent, current: Node, newNode: VNode) =
|
proc updateDirtyElements(parent, current: Node, newNode: VNode) =
|
||||||
if newNode.key >= 0 and isDirty(newNode.key):
|
if newNode.key >= 0 and isDirty(newNode.key):
|
||||||
|
|
@ -162,26 +154,68 @@ proc updateDirtyElements(parent, current: Node, newNode: VNode) =
|
||||||
#if dirtyCount <= 0: return
|
#if dirtyCount <= 0: return
|
||||||
|
|
||||||
proc printChildren(parent: Node): cstring =
|
proc printChildren(parent: Node): cstring =
|
||||||
if parent != nil and parent.hasChildNodes:
|
discard
|
||||||
var it = parent.firstChild
|
# if parent != nil and parent.hasChildNodes:
|
||||||
result = ""
|
# var it = parent.firstChild
|
||||||
while it != nil:
|
# result = ""
|
||||||
if it.id == nil:
|
# while it != nil:
|
||||||
result.add(" nil")
|
# if it.id == nil:
|
||||||
else:
|
# result.add(" nil")
|
||||||
result.add(" " & $it.id)
|
# else:
|
||||||
it = it.nextSibling
|
# result.add(" " & $it.id)
|
||||||
|
# it = it.nextSibling
|
||||||
|
|
||||||
proc printChildren(parent: VNode): cstring =
|
proc printChildren(parent: VNode): cstring =
|
||||||
if parent != nil:
|
discard
|
||||||
result = ""
|
# if parent != nil:
|
||||||
for i in 0..parent.len-1:
|
# result = ""
|
||||||
if parent[i] == nil or parent[i].id == nil:
|
# for i in 0..parent.len-1:
|
||||||
kout cstring("nil")
|
# if parent[i] == nil or parent[i].id == nil:
|
||||||
else:
|
# result.add(" nil")
|
||||||
result.add(" " & $(parent[i].id))
|
# else:
|
||||||
|
# result.add(" " & $(parent[i].id))
|
||||||
|
|
||||||
proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
proc print(s: cstring, ident: int) =
|
||||||
|
discard
|
||||||
|
# var result = ""
|
||||||
|
# for i in 0..ident:
|
||||||
|
# result.add " "
|
||||||
|
# result.add(s)
|
||||||
|
# kout cstring(result)
|
||||||
|
|
||||||
|
proc longestIncreasingSubsequence(a: seq[int]): seq[int] =
|
||||||
|
result.add 0
|
||||||
|
var parent = newSeq[int](len(a))
|
||||||
|
for i in 0..<len(a):
|
||||||
|
var j = result[len(result) - 1]
|
||||||
|
if a[j] < a[i]:
|
||||||
|
parent[i] = j
|
||||||
|
result.add(i)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var left = 0
|
||||||
|
var right = len(result) - 1
|
||||||
|
|
||||||
|
while left < right:
|
||||||
|
var mid = (left + right) div 2
|
||||||
|
if a[result[mid]] < a[i]:
|
||||||
|
left = mid + 1
|
||||||
|
else:
|
||||||
|
right = mid
|
||||||
|
|
||||||
|
if a[i] < a[result[left]]:
|
||||||
|
if left > 0:
|
||||||
|
parent[i] = result[left - 1]
|
||||||
|
result[left] = i
|
||||||
|
|
||||||
|
var pos = len(result)
|
||||||
|
var v = result[pos - 1]
|
||||||
|
while pos > 0:
|
||||||
|
result[pos] = v
|
||||||
|
v = parent[v]
|
||||||
|
dec pos
|
||||||
|
|
||||||
|
proc updateElement(parent, current: Node, newNode, oldNode: VNode, ident: int = 0) =
|
||||||
newNode.dom = oldNode.dom
|
newNode.dom = oldNode.dom
|
||||||
if not equalsShallow(newNode, oldNode):
|
if not equalsShallow(newNode, oldNode):
|
||||||
detach(oldNode)
|
detach(oldNode)
|
||||||
|
|
@ -207,21 +241,21 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
||||||
current.removeChild(current.lastChild)
|
current.removeChild(current.lastChild)
|
||||||
else:
|
else:
|
||||||
#kout cstring("start")
|
#kout cstring("start")
|
||||||
kout cstring("----------------")
|
print("----------------", ident)
|
||||||
kout cstring("----------------")
|
print("----------------", ident)
|
||||||
var before = printChildren(current)
|
var before = printChildren(current)
|
||||||
|
|
||||||
# maximal common prefix
|
# maximal common prefix
|
||||||
var left = 0
|
var left = 0
|
||||||
while left < minLength and equalsShallow(newNode[left], oldNode[left]):
|
while left < minLength and equalsShallow(newNode[left], oldNode[left]):
|
||||||
updateElement(current, oldNode[left].dom, newNode[left], oldNode[left])
|
updateElement(current, oldNode[left].dom, newNode[left], oldNode[left], ident + 1)
|
||||||
inc left
|
inc left
|
||||||
|
|
||||||
# maximal common suffix
|
# maximal common suffix
|
||||||
var rightOld = oldLength - 1
|
var rightOld = oldLength - 1
|
||||||
var rightNew = newLength - 1
|
var rightNew = newLength - 1
|
||||||
while rightOld >= left and rightNew >= left and equalsShallow(newNode[rightNew], oldNode[rightOld]):
|
while rightOld >= left and rightNew >= left and equalsShallow(newNode[rightNew], oldNode[rightOld]):
|
||||||
updateElement(current, oldNode[rightOld].dom, newNode[rightNew], oldNode[rightOld])
|
updateElement(current, oldNode[rightOld].dom, newNode[rightNew], oldNode[rightOld], ident + 1)
|
||||||
dec rightOld
|
dec rightOld
|
||||||
dec rightNew
|
dec rightNew
|
||||||
|
|
||||||
|
|
@ -231,118 +265,140 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
|
||||||
|
|
||||||
var flag = false
|
var flag = false
|
||||||
#if rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
|
#if rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
|
||||||
kout cstring("current")
|
print("current", ident)
|
||||||
kout printChildren(current)
|
print(printChildren(current), ident)
|
||||||
kout cstring("oldNode")
|
print("oldNode", ident)
|
||||||
kout printChildren(oldNode)
|
print(printChildren(oldNode), ident)
|
||||||
kout cstring("newNode")
|
print("newNode", ident)
|
||||||
kout printChildren(newNode)
|
print(printChildren(newNode), ident)
|
||||||
flag = true
|
flag = true
|
||||||
|
|
||||||
var step = 1
|
# cross comparing
|
||||||
while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
|
while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
|
||||||
kout cstring($oldNode[leftOld].id & " " & $newNode[rightNew].id)
|
print($oldNode[leftOld].id & " " & $newNode[rightNew].id, ident)
|
||||||
kout cstring("pos: " & $leftOld & " " & $rightNew)
|
print("pos: " & $leftOld & " " & $rightNew, ident)
|
||||||
|
|
||||||
var nextNode: Node = nil
|
var nextNode: Node = nil
|
||||||
if rightNew + 1 < newLength:
|
if rightNew + 1 < newLength:
|
||||||
nextNode = newNode[rightNew + 1].dom
|
nextNode = newNode[rightNew + 1].dom
|
||||||
|
print("update", ident)
|
||||||
updateElement(current, oldNode[leftOld].dom, newNode[rightNew], oldNode[leftOld])
|
updateElement(current, oldNode[leftOld].dom, newNode[rightNew], oldNode[leftOld], ident + 1)
|
||||||
|
print("update", ident)
|
||||||
if nextNode == nil:
|
if nextNode == nil:
|
||||||
current.appendChild(oldNode[leftOld].dom)
|
current.appendChild(oldNode[leftOld].dom)
|
||||||
kout cstring("append")
|
print("append", ident)
|
||||||
else:
|
else:
|
||||||
kout cstring("insertBefore")
|
print("insertBefore", ident)
|
||||||
current.insertBefore(oldNode[leftOld].dom, nextNode)
|
current.insertBefore(oldNode[leftOld].dom, nextNode)
|
||||||
#kout cstring("step = " & $step & "; leftOld = " & $leftOld & "; rightNew = " & $rightNew)
|
print($oldNode[leftOld], ident)
|
||||||
kout cstring($oldNode[leftOld])
|
|
||||||
inc step
|
|
||||||
inc leftOld
|
inc leftOld
|
||||||
dec rightNew
|
dec rightNew
|
||||||
|
|
||||||
|
while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[rightOld], newNode[leftNew]):
|
||||||
|
var nextNode: Node = oldNode[leftOld].dom
|
||||||
|
updateElement(current, oldNode[rightOld].dom, newNode[leftNew], oldNode[rightOld], ident + 1)
|
||||||
|
current.insertBefore(oldNode[rightOld].dom, nextNode)
|
||||||
|
inc leftNew
|
||||||
|
dec rightOld
|
||||||
|
|
||||||
if flag:
|
if flag:
|
||||||
kout cstring("after")
|
print("after", ident)
|
||||||
kout printChildren(current)
|
print(printChildren(current), ident)
|
||||||
|
|
||||||
|
|
||||||
while rightOld >= leftOld and rightNew >= leftNew:
|
var isKeyed = true
|
||||||
updateElement(current, current[leftOld], newNode[leftNew], oldNode[leftOld])
|
for i in leftNew..rightNew:
|
||||||
inc leftNew
|
if newNode[i].key == -1:
|
||||||
inc leftOld
|
isKeyed = false
|
||||||
|
break
|
||||||
var isPushBack = (rightNew + 1 == newLength)
|
|
||||||
var nextNode: Node = nil
|
|
||||||
if not isPushBack:
|
|
||||||
nextNode = newNode[rightNew + 1].dom
|
|
||||||
while leftNew <= rightNew:
|
|
||||||
var node = vnodeToDom(newNode[leftNew])ды
|
|
||||||
if isPushBack:
|
|
||||||
current.appendChild(node)
|
|
||||||
else:
|
|
||||||
current.insertBefore(node, nextNode)
|
|
||||||
inc leftNew
|
|
||||||
|
|
||||||
for i in leftOld..rightOld:
|
for i in leftOld..rightOld:
|
||||||
current.removeChild(oldNode[i].dom)
|
if oldNode[i].key == -1:
|
||||||
detach(oldNode[i])
|
isKeyed = false
|
||||||
|
if not isKeyed:
|
||||||
|
break
|
||||||
|
|
||||||
kout cstring("----------------")
|
if isKeyed:
|
||||||
kout cstring("before")
|
if rightNew > leftNew:
|
||||||
kout before
|
# remove redundant old nodes
|
||||||
kout cstring("finish")
|
for i in leftOld..rightOld:
|
||||||
kout printChildren(current)
|
current.removeChild(oldNode[i].dom)
|
||||||
kout cstring("----------------")
|
detach(oldNode[i])
|
||||||
kout cstring("----------------")
|
else:
|
||||||
|
# permute elements using LIS
|
||||||
|
var positionByKey = newTable[VKey, int]()
|
||||||
|
var positions = newSeq[int]()
|
||||||
|
for i in leftOld..rightOld:
|
||||||
|
positionByKey[oldNode[i].key] = i
|
||||||
|
for i in leftNew..rightNew:
|
||||||
|
if positionByKey.hasKey(newNode[i].key):
|
||||||
|
positions.add positionByKey[newNode[i].key]
|
||||||
|
|
||||||
|
var lis = longestIncreasingSubsequence(positions)
|
||||||
|
var lisPos = 0
|
||||||
|
var isNotRedundant = newSeq[bool](rightOld - leftOld + 1)
|
||||||
|
for i in leftNew..rightNew:
|
||||||
|
if lisPos < len(lis):
|
||||||
|
var index = lis[lisPos]
|
||||||
|
if oldNode[index].key == newNode[i].key:
|
||||||
|
isNotRedundant[index - leftOld] = true
|
||||||
|
updateElement(current, oldNode[index].dom, newNode[i], oldNode[index], ident + 1)
|
||||||
|
inc lisPos
|
||||||
|
else:
|
||||||
|
if positionByKey.hasKey(newNode[i].key):
|
||||||
|
var oldPos = positionByKey[newNode[i].key]
|
||||||
|
isNotRedundant[oldPos - leftOld] = true
|
||||||
|
current.insertBefore(oldNode[oldPos].dom, oldNode[index].dom)
|
||||||
|
else:
|
||||||
|
current.insertBefore(vnodeToDom(newNode[i]), oldNode[index].dom)
|
||||||
|
else:
|
||||||
|
if positionByKey.hasKey(newNode[i].key):
|
||||||
|
var oldPos = positionByKey[newNode[i].key]
|
||||||
|
isNotRedundant[oldPos - leftOld] = true
|
||||||
|
current.appendChild(oldNode[oldPos].dom)
|
||||||
|
else:
|
||||||
|
current.appendChild(vnodeToDom(newNode[i]))
|
||||||
|
|
||||||
|
# remove redundant old nodes
|
||||||
|
for i in leftOld..rightOld:
|
||||||
|
if not isNotRedundant[i]:
|
||||||
|
current.removeChild(oldNode[i].dom)
|
||||||
|
else:
|
||||||
|
# simply diff
|
||||||
|
print($leftOld & " " & $rightOld & " " & $leftNew & " " & $rightNew, ident)
|
||||||
|
while rightOld >= leftOld and rightNew >= leftNew:
|
||||||
|
updateElement(current, oldNode[leftOld].dom, newNode[leftNew], oldNode[leftOld], ident + 1)
|
||||||
|
inc leftNew
|
||||||
|
inc leftOld
|
||||||
|
|
||||||
|
print("TEMP", ident)
|
||||||
|
print(printChildren(current), ident)
|
||||||
|
print($leftOld & " " & $rightOld & " " & $leftNew & " " & $rightNew, ident)
|
||||||
|
print("other part start", ident)
|
||||||
|
var isPushBack = (rightNew + 1 == newLength)
|
||||||
|
var nextNode: Node = nil
|
||||||
|
if not isPushBack:
|
||||||
|
print($(rightNew + 1), ident)
|
||||||
|
nextNode = newNode[rightNew + 1].dom
|
||||||
|
while leftNew <= rightNew:
|
||||||
|
var node = vnodeToDom(newNode[leftNew])
|
||||||
|
if isPushBack:
|
||||||
|
current.appendChild(node)
|
||||||
|
else:
|
||||||
|
current.insertBefore(node, nextNode)
|
||||||
|
inc leftNew
|
||||||
|
print("other part finish", ident)
|
||||||
|
|
||||||
# var leftNew = left
|
for i in leftOld..rightOld:
|
||||||
# var leftOld = left
|
current.removeChild(oldNode[i].dom)
|
||||||
# while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
|
detach(oldNode[i])
|
||||||
# var nextNode: Node = nil
|
|
||||||
# if rightNew + 1 < newLength:
|
|
||||||
# nextNode = newNode[rightNew + 1].dom
|
|
||||||
# if nextNode == nil:
|
|
||||||
# current.appendChild(oldNode[leftOld].dom)
|
|
||||||
# else:
|
|
||||||
# current.insertBefore(oldNode[leftOld].dom, nextNode)
|
|
||||||
# inc leftOld
|
|
||||||
# dec rightNew
|
|
||||||
|
|
||||||
# var lastTarget: Node = nil
|
print("----------------", ident)
|
||||||
# if leftOld <= rightOld:
|
print("before", ident)
|
||||||
# lastTarget = oldNode[leftOld].dom
|
print(before, ident)
|
||||||
# while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[rightOld], newNode[leftNew]):
|
print("finish", ident)
|
||||||
# current.insertBefore(oldNode[rightOld].dom, lastTarget)
|
print(printChildren(current), ident)
|
||||||
# lastTarget = oldNode[rightOld].dom
|
print("----------------", ident)
|
||||||
# inc leftNew
|
print("----------------", ident)
|
||||||
# dec rightOld
|
|
||||||
|
|
||||||
# if rightOld < leftOld:
|
|
||||||
# var isPushBack = leftOld < oldLength
|
|
||||||
# for i in leftNew..rightNew:
|
|
||||||
# if isPushBack:
|
|
||||||
# current.appendChild(vnodeToDom(newNode[i]))
|
|
||||||
# else:
|
|
||||||
# current.insertBefore(vnodeToDom(newNode[i]), oldNode[leftOld].dom)
|
|
||||||
# elif rightNew < leftNew:
|
|
||||||
# for i in leftOld..rightOld:
|
|
||||||
# current.removeChild(oldNode[i].dom)
|
|
||||||
# detach(oldNode[i])
|
|
||||||
# else:
|
|
||||||
# while rightOld >= leftOld and rightNew >= leftNew:
|
|
||||||
# updateElement(current, oldNode[leftOld].dom, newNode[leftNew], oldNode[leftOld])
|
|
||||||
# inc leftNew
|
|
||||||
# inc leftOld
|
|
||||||
# if rightOld < leftOld:
|
|
||||||
# while rightNew >= leftNew:
|
|
||||||
# current.appendChild(vnodeToDom(newNode[leftNew]))
|
|
||||||
# inc leftNew
|
|
||||||
# elif rightNew < leftNew:
|
|
||||||
# while rightOld >= leftOld:
|
|
||||||
# current.removeChild(oldNode[leftOld].dom)
|
|
||||||
# detach(oldNode[leftOld])
|
|
||||||
# inc leftOld
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
var drawTimeout: Timeout
|
var drawTimeout: Timeout
|
||||||
|
|
|
||||||
|
|
@ -147,20 +147,20 @@ proc onload() =
|
||||||
var dtTest = 500
|
var dtTest = 500
|
||||||
|
|
||||||
var t = dtReset
|
var t = dtReset
|
||||||
# timeout = setTimeout(test1, t)
|
timeout = setTimeout(test1, t)
|
||||||
# t += dtTest
|
t += dtTest
|
||||||
# timeout = setTimeout(reset, t)
|
timeout = setTimeout(reset, t)
|
||||||
# t += dtReset
|
t += dtReset
|
||||||
|
|
||||||
# timeout = setTimeout(test2, t)
|
timeout = setTimeout(test2, t)
|
||||||
# t += dtTest
|
t += dtTest
|
||||||
# timeout = setTimeout(reset, t)
|
timeout = setTimeout(reset, t)
|
||||||
# t += dtReset
|
t += dtReset
|
||||||
|
|
||||||
# timeout = setTimeout(test3, t)
|
timeout = setTimeout(test3, t)
|
||||||
# t += dtTest
|
t += dtTest
|
||||||
# timeout = setTimeout(reset, t)
|
timeout = setTimeout(reset, t)
|
||||||
# t += dtReset
|
t += dtReset
|
||||||
|
|
||||||
timeout = setTimeout(test4, t)
|
timeout = setTimeout(test4, t)
|
||||||
t += dtTest
|
t += dtTest
|
||||||
|
|
@ -172,12 +172,12 @@ proc onload() =
|
||||||
timeout = setTimeout(reset, t)
|
timeout = setTimeout(reset, t)
|
||||||
t += dtReset
|
t += dtReset
|
||||||
|
|
||||||
# timeout = setTimeout(test6, t)
|
timeout = setTimeout(test6, t)
|
||||||
# t += dtTest
|
t += dtTest
|
||||||
# timeout = setTimeout(reset, t)
|
timeout = setTimeout(reset, t)
|
||||||
# t += dtReset
|
t += dtReset
|
||||||
|
|
||||||
# timeout = setTimeout(test7, t)
|
timeout = setTimeout(test7, t)
|
||||||
|
|
||||||
onload()
|
onload()
|
||||||
setRenderer createDom
|
setRenderer createDom
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue