Hi, I found following two codes output different values.
import tables
var n = 3
var dp = initTable[int, int](0)
proc dfs(x: int): int =
if x < 2: return 0
if x in dp: return dp[x]
dp[x] = x
dp[x] += dfs(x div 2)
dp[x] += dfs((x+1) div 2)
return dp[x]
echo dfs(n) # 3
import tables
var n = 3
var dp = initTable[int, int](0)
proc dfs(x: int): int =
if x < 2: return 0
if x in dp: return dp[x]
dp[x] = x
var y = dfs(x div 2)
dp[x] += y
y = dfs((x+1) div 2)
dp[x] += y
return dp[x]
echo dfs(n) # 5
Why these codes returns different reaults?
After digging around a bit more, I found even weirder results. when I fix initial size of hash table, it returns different value. When I modify line 3 of my original code to
var dp = initTable[int, int](4)
so that resize should not be occur, and it also returns 5, which isn't the same as my first result.
Modifying line 3 to
var dp = newSeq[int](4)
also returns 5, so I suspect something occured while resizing hash table.
I'll also try gdb debugging, but post just as a quick note.
That's definitely seems like a bug and a wild one at that. Please report it on github. Here are my findings:
import tables
var t = initTable[int, int](0)
proc foo(x: int): int =
if x < 2: return 0
t[x] = x
t[x] = t[x] + foo(x-1)
if x == 2: echo t.repr # this seemingly benign line affects the result
return t[x]
echo foo(3) # 3832898853807797816
import tables
var t = initTable[int, int](0)
proc foo(x: int): int =
if x < 2: return 0
t[x] = x
t[x] = t[x] + foo(x-1)
return t[x]
echo foo(3) # 2
Culprit is the initTable(0), any of these fix the issue:
var t: Table[int, int]
var t: initTable[int, int]()
var t: initTable[int, int](x) # where x > 0
dp[x] += dfs(x div 2)
I think using temporal variable like var y = dfs(x div 2) in your code is easiest way to workaround this.
Simpler code to reproduce this bug:
import tables
var dp = initTable[int, int](0)
proc test(x: var int; y: int) =
echo "In test proc, addr x = ", cast[uint](addr x)
echo "In test proc, addr dp[0] = ", cast[uint](addr dp[0])
x += y
dp[0] = 1
test(dp[0], (block:
dp[1] = 1 # Cause reallocation
10))
echo "After test, addr dp[0] = ", cast[uint](addr dp[0])
echo dp[0]
echo dp[1]
Output:
In test proc, addr x = 140048628686976
In test proc, addr dp[0] = 140048628691072
After test, addr dp[0] = 140048628691072
1
1