$print {
/**
  Running hash table tests...
*/
}

const each = proc {k,v} {
  //assert ! [this.mayIterate] // fixme: why does this _not_ fail?
  $print '  ' this '\t' typename k k '\t=\t' typename v v
  //$print 'this =' this this.search this.insert
  if(!argv.callee.checked){
    argv.callee.checked = 
        assert 'CWAL_RC_ACCESS' === [catch{$this.insert "MUST FAIL" true}.codeString]
    $print "Modification during iteration properly failed."
  }
}

0 && scope {
  // check valgrind...
  const h [api.Hash 10000]
  assert 'hash' === typename h
}


1 && scope {
  $print "Hashtable #1..."
  const h = [api.Hash 19];
  assert [h.mayIterate]
  //$print 'h =' h h.insert h.search
  [h.insert "foo" "bar"]
  assert [h.containsEntry "foo"]
  assert ![h.containsEntry "bar"]
  [h.insert "foo" "baz"]
  h.foo = "the other foo"
  $print 'get foo =' [h.search "foo"]
  assert "the other foo" === h.foo
  assert "baz" === [h.search "foo"]

  $print h "table entries:"
  $h.eachEntry each

  assert 'function' === typename h.prototype.entryKeys
  assert 'function' === typename h.prototype.prototype.propertyKeys
  assert h.prototype.prototype.propertyKeys !== h.entryKeys
  
  var pk = h.prototype.prototype.propertyKeys
  assert 'function' === typename pk.apply
  var pkeys = [pk.apply h]
  $print 'pk.apply... ' pkeys
  assert "the other foo" === h.(pkeys.0)

  $print "ending scope"
}

0 && scope {
  $print "Hashtable #2..."
  var h;
  scope {
    h = [api.Hash 400];
    assert (400 < [h.hashSize]) && (600 > [h.hashSize])
    [h.insert "foo" "bar"]
    [h.insert "foot" "baz"]
    $print h "table entries:"
    [h.eachEntry each]
    [h.remove "foo"]
    [h.remove "foot"]
    $print "hash =" h
  }
  $print "ending scope" h
}

1 && scope {
  $print "Hashtable #N..."
  const h = [api.Hash 19];
  assert 1 === refcount h
  if(false){
    [[[h.insert "1" "one"].
      insert 1 "!"].
      insert h h] /* ugly, huh? */
  }else{
    [h.insert "1" "one"]
    [h.insert 1 "!"]
    [h.insert h h]
  }

  assert 3 === refcount h
  assert 3 === [h.entryCount]
  [h.eachEntry each]
  var keys = [h.entryKeys], vals = [h.entryValues]
  assert 'array' === typename keys
  assert 3 === [keys.length]
  assert 'array' === typename vals
  assert 3 === [vals.length]

  assert vals.1 === [h.search keys.1]


  $print h "table entries:"
  [h.eachEntry each]
  assert "one" === [h.search "1"]
  assert "!" === [h.search 1]
  const oldRc = refcount h
  [h.remove h]
  assert (oldRc-2) === refcount h
  assert 2 === [h.entryCount]

  $print "Hash table properties again:"
  [h.eachEntry each]
  keys = [h.entryKeys]
  vals = [h.entryValues]
  $print 'keys =' keys
  $print 'values =' vals
  assert 2 === [keys.length]
  assert 2 === [vals.length]
  [h.clearEntries]
  assert 0 === [h.entryCount]
  //$print "Hash table properties after clearing:"
  //[h.eachEntry each]
  //$print "(should be empty)"
}

1 && scope {
  const obj object { a: 3, b: 5 }
  const h [api.Hash]
  $print "Hashtable #X..." h
  h.cpProp = proc{from}{
    const self = this
    $from.eachProperty proc{k,v}{
      [self.insert k v]
    }
  }
  [h.cpProp obj]
  assert 2 === [h.entryCount]
  assert [h.entryValues].1 === obj.([h.entryKeys].1)
  //$print "Copied properties:"
  //[h.eachEntry each]
  [h.insert 'a' 7]
  assert 2 === [h.entryCount]
  //$print "Modified copied properties:"
  //[h.eachEntry each]
  assert [h.search 'a'] !== obj.a

  var n = 0
  h.eachEntry(proc(){
      n += 1
      return false
  })
  assert 1 === n

  n = 0
  h.eachProperty(proc(){ n+=1; return false})
  assert 1 === n

}

scope {
  const ar = array[1,2,3,4]
  var n = 0
  ar.eachIndex(proc(){ return !((n += 1)===2)})
  assert 2===n

}

scope {
  const obj = object{
    c:3, a:1, b:2
  }
  $api.ob.push
  $print obj
  const v1 = $api.ob.takeBuffer
  [obj.sortProperties]
  [print obj]
  const v2 = $api.ob.takeBuffer
  $api.ob.pop
  assert v2 !== v1 /* this ACTUALLY does not necessarily hold
    because the original _might_ (but probably was not)
    accidentally sorted. */

  // And to prove that the above comparison in valid...
  var b1 = api.Buffer(10), b2 = api.Buffer(10)
  $b1.append "hi"
  $b2.append "hi"
  assert b1 === b2
  $b2.append "!"
  assert b1 !== b2


  assert 'CWAL_RC_ACCESS' === [catch{
    $obj.eachProperty proc(){ [obj.sortProperties] }
  }.codeString /* cannot sort while traversing */]
}

0 && scope {
  // check valgrind...
  const h [api.Hash 1000]
  assert 'hash' === typename h
  assert 1000 <= [h.hashSize]
}

$print "done"
return "hoorah for hashes!"
