discard """
  output: "ok"
"""

import tables, lists

type
  ListTable[K, V] = object
    valList: DoublyLinkedList[V]
    table: Table[K, DoublyLinkedNode[V]]

  ListTableRef*[K, V] = ref ListTable[K, V]

proc initListTable*[K, V](initialSize = 64): ListTable[K, V] =
  result.valList = initDoublyLinkedList[V]()
  result.table = initTable[K, DoublyLinkedNode[V]]()

proc newListTable*[K, V](initialSize = 64): ListTableRef[K, V] =
  new(result)
  result[] = initListTable[K, V](initialSize)

proc `[]=`*[K, V](t: var ListTable[K, V], key: K, val: V) =
  if key in t.table:
    t.table[key].value = val
  else:
    let node = newDoublyLinkedNode(val)
    t.valList.append(node)
    t.table[key] = node

proc `[]`*[K, V](t: ListTable[K, V], key: K): var V {.inline.} =
  result = t.table[key].value

proc len*[K, V](t: ListTable[K, V]): Natural {.inline.} =
  result = t.table.len

iterator values*[K, V](t: ListTable[K, V]): V =
  for val in t.valList.items():
    yield val

proc `[]=`*[K, V](t: ListTableRef[K, V], key: K, val: V) =
  t[][key] = val

proc `[]`*[K, V](t: ListTableRef[K, V], key: K): var V {.inline.} =
  t[][key]

proc len*[K, V](t: ListTableRef[K, V]): Natural {.inline.} =
  t[].len

iterator values*[K, V](t: ListTableRef[K, V]): V =
  for val in t[].values:
    yield val

proc main() =
  type SomeObj = ref object

  for outer in 0..10_000:
    let myObj = new(SomeObj)
    let table = newListTable[int, SomeObj]()

    table[0] = myObj
    for i in 1..100:
      table[i] = new(SomeObj)

    var myObj2: SomeObj
    for val in table.values():
      if myObj2.isNil:
        myObj2 = val
    assert(myObj == myObj2) # passes

    var tableCopy: ListTableRef[int, SomeObj]
    deepCopy(tableCopy, table)

    let myObjCopy = tableCopy[0]
    var myObjCopy2: SomeObj = nil
    for val in tableCopy.values():
      if myObjCopy2.isNil:
        myObjCopy2 = val

    #echo cast[int](myObj)
    #echo cast[int](myObjCopy)
    #echo cast[int](myObjCopy2)

    assert(myObjCopy == myObjCopy2) # fails


type
  PtrTable = object
    counter, max: int
    data: array[0..99, (pointer, pointer)]

assert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)

main()
echo "ok"
