Add LIS opt

This commit is contained in:
Roman Vishnevskii 2017-05-10 17:38:51 +07:00
commit ae448f947f
2 changed files with 208 additions and 152 deletions

View file

@ -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,6 +123,9 @@ 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.key == -1 and b.key == -1:
return eq(a, b)
else:
if a.kind != b.kind: return false if a.kind != b.kind: return false
if a.id != b.id: return false if a.id != b.id: return false
if a.key != b.key: return false if a.key != b.key: return false
@ -135,17 +138,6 @@ proc equalsShallow(a, b: VNode): bool =
# XXX test event listeners here? # XXX test event listeners here?
return true 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:
result = eq(a, b)
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):
unmarkDirty(newNode.key) unmarkDirty(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)
var isKeyed = true
for i in leftNew..rightNew:
if newNode[i].key == -1:
isKeyed = false
break
for i in leftOld..rightOld:
if oldNode[i].key == -1:
isKeyed = false
if not isKeyed:
break
if isKeyed:
if rightNew > leftNew:
# remove redundant old nodes
for i in leftOld..rightOld:
current.removeChild(oldNode[i].dom)
detach(oldNode[i])
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: while rightOld >= leftOld and rightNew >= leftNew:
updateElement(current, current[leftOld], newNode[leftNew], oldNode[leftOld]) updateElement(current, oldNode[leftOld].dom, newNode[leftNew], oldNode[leftOld], ident + 1)
inc leftNew inc leftNew
inc leftOld 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 isPushBack = (rightNew + 1 == newLength)
var nextNode: Node = nil var nextNode: Node = nil
if not isPushBack: if not isPushBack:
print($(rightNew + 1), ident)
nextNode = newNode[rightNew + 1].dom nextNode = newNode[rightNew + 1].dom
while leftNew <= rightNew: while leftNew <= rightNew:
var node = vnodeToDom(newNode[leftNew])ды var node = vnodeToDom(newNode[leftNew])
if isPushBack: if isPushBack:
current.appendChild(node) current.appendChild(node)
else: else:
current.insertBefore(node, nextNode) current.insertBefore(node, nextNode)
inc leftNew inc leftNew
print("other part finish", ident)
for i in leftOld..rightOld: for i in leftOld..rightOld:
current.removeChild(oldNode[i].dom) current.removeChild(oldNode[i].dom)
detach(oldNode[i]) detach(oldNode[i])
kout cstring("----------------") print("----------------", ident)
kout cstring("before") print("before", ident)
kout before print(before, ident)
kout cstring("finish") print("finish", ident)
kout printChildren(current) print(printChildren(current), ident)
kout cstring("----------------") print("----------------", ident)
kout cstring("----------------") print("----------------", ident)
# var leftNew = left
# var leftOld = left
# while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[leftOld], newNode[rightNew]):
# 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
# if leftOld <= rightOld:
# lastTarget = oldNode[leftOld].dom
# while rightOld >= leftOld and rightNew >= leftNew and equalsShallow(oldNode[rightOld], newNode[leftNew]):
# current.insertBefore(oldNode[rightOld].dom, lastTarget)
# lastTarget = oldNode[rightOld].dom
# inc leftNew
# 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

View file

@ -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