Skip to content

Commit

Permalink
add simple bimap
Browse files Browse the repository at this point in the history
  • Loading branch information
dsainati1 committed Sep 11, 2023
1 parent e0407b6 commit d4fc4c5
Show file tree
Hide file tree
Showing 2 changed files with 311 additions and 0 deletions.
105 changes: 105 additions & 0 deletions runtime/common/bimap/bimap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Cadence - The resource-oriented smart contract programming language
*
* Copyright Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Based on https://github.com/vishalkuo/bimap, Copyright Vishal Kuo
*
*/

package bimap

type BiMap[K comparable, V comparable] struct {
forward map[K]V
backward map[V]K
}

// NewBiMap returns a an empty, mutable, biMap
func NewBiMap[K comparable, V comparable]() *BiMap[K, V] {
return &BiMap[K, V]{forward: make(map[K]V), backward: make(map[V]K)}
}

// NewBiMapFrom returns a new BiMap from a map[K, V]
func NewBiMapFromMap[K comparable, V comparable](forwardMap map[K]V) *BiMap[K, V] {
biMap := NewBiMap[K, V]()
for k, v := range forwardMap {
biMap.Insert(k, v)
}
return biMap
}

// Insert puts a key and value into the BiMap, and creates the reverse mapping from value to key.
func (b *BiMap[K, V]) Insert(k K, v V) {
if _, ok := b.forward[k]; ok {
delete(b.backward, b.forward[k])
}
b.forward[k] = v
b.backward[v] = k
}

// Exists checks whether or not a key exists in the BiMap
func (b *BiMap[K, V]) Exists(k K) bool {
_, ok := b.forward[k]
return ok
}

// ExistsInverse checks whether or not a value exists in the BiMap
func (b *BiMap[K, V]) ExistsInverse(k V) bool {
_, ok := b.backward[k]
return ok
}

// Get returns the value for a given key in the BiMap and whether or not the element was present.
func (b *BiMap[K, V]) Get(k K) (V, bool) {
if !b.Exists(k) {
return *new(V), false
}
return b.forward[k], true
}

// GetInverse returns the key for a given value in the BiMap and whether or not the element was present.
func (b *BiMap[K, V]) GetInverse(v V) (K, bool) {
if !b.ExistsInverse(v) {
return *new(K), false
}
return b.backward[v], true
}

// Delete removes a key-value pair from the BiMap for a given key. Returns if the key doesn't exist
func (b *BiMap[K, V]) Delete(k K) {
if !b.Exists(k) {
return
}
val, _ := b.Get(k)
delete(b.forward, k)
delete(b.backward, val)
}

// DeleteInverse emoves a key-value pair from the BiMap for a given value. Returns if the value doesn't exist
func (b *BiMap[K, V]) DeleteInverse(v V) {
if !b.ExistsInverse(v) {
return
}

key, _ := b.GetInverse(v)
delete(b.backward, v)
delete(b.forward, key)

}

// Size returns the number of elements in the bimap
func (b *BiMap[K, V]) Size() int {
return len(b.forward)
}
206 changes: 206 additions & 0 deletions runtime/common/bimap/bimap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* Cadence - The resource-oriented smart contract programming language
*
* Copyright Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package bimap

import (
"testing"

"github.com/stretchr/testify/assert"
)

const key = "key"
const value = "value"

func TestNewBiMap(t *testing.T) {
actual := NewBiMap[string, string]()
expected := &BiMap[string, string]{forward: make(map[string]string), backward: make(map[string]string)}
assert.Equal(t, expected, actual, "They should be equal")
}

func TestNewBiMapFrom(t *testing.T) {
actual := NewBiMapFromMap(map[string]string{
key: value,
})
actual.Insert(key, value)

fwdExpected := make(map[string]string)
invExpected := make(map[string]string)
fwdExpected[key] = value
invExpected[value] = key
expected := &BiMap[string, string]{forward: fwdExpected, backward: invExpected}

assert.Equal(t, expected, actual, "They should be equal")
}

func TestBiMap_Insert(t *testing.T) {
actual := NewBiMap[string, string]()
actual.Insert(key, value)

fwdExpected := make(map[string]string)
invExpected := make(map[string]string)
fwdExpected[key] = value
invExpected[value] = key
expected := &BiMap[string, string]{forward: fwdExpected, backward: invExpected}

assert.Equal(t, expected, actual, "They should be equal")
}

func TestBiMap_InsertTwice(t *testing.T) {
additionalValue := value + value

actual := NewBiMap[string, string]()
actual.Insert(key, value)
actual.Insert(key, additionalValue)

fwdExpected := make(map[string]string)
invExpected := make(map[string]string)
fwdExpected[key] = additionalValue

invExpected[additionalValue] = key
expected := &BiMap[string, string]{forward: fwdExpected, backward: invExpected}

assert.Equal(t, expected, actual, "They should be equal")
}

func TestBiMap_Exists(t *testing.T) {
actual := NewBiMap[string, string]()

actual.Insert(key, value)
assert.False(t, actual.Exists("ARBITARY_KEY"), "Key should not exist")
assert.True(t, actual.Exists(key), "Inserted key should exist")
}

func TestBiMap_InverseExists(t *testing.T) {
actual := NewBiMap[string, string]()

actual.Insert(key, value)
assert.False(t, actual.ExistsInverse("ARBITARY_VALUE"), "Value should not exist")
assert.True(t, actual.ExistsInverse(value), "Inserted value should exist")
}

func TestBiMap_Get(t *testing.T) {
actual := NewBiMap[string, string]()

actual.Insert(key, value)

actualVal, ok := actual.Get(key)

assert.True(t, ok, "It should return true")
assert.Equal(t, value, actualVal, "Value and returned val should be equal")

actualVal, ok = actual.Get(value)

assert.False(t, ok, "It should return false")
assert.Empty(t, actualVal, "Actual val should be empty")
}

func TestBiMap_GetInverse(t *testing.T) {
actual := NewBiMap[string, string]()

actual.Insert(key, value)

actualKey, ok := actual.GetInverse(value)

assert.True(t, ok, "It should return true")
assert.Equal(t, key, actualKey, "Key and returned key should be equal")

actualKey, ok = actual.Get(value)

assert.False(t, ok, "It should return false")
assert.Empty(t, actualKey, "Actual key should be empty")
}

func TestBiMap_Size(t *testing.T) {
actual := NewBiMap[string, string]()

assert.Equal(t, 0, actual.Size(), "Length of empty bimap should be zero")

actual.Insert(key, value)

assert.Equal(t, 1, actual.Size(), "Length of bimap should be one")
}

func TestBiMap_Delete(t *testing.T) {
actual := NewBiMap[string, string]()
dummyKey := "DummyKey"
dummyVal := "DummyVal"
actual.Insert(key, value)
actual.Insert(dummyKey, dummyVal)

assert.Equal(t, 2, actual.Size(), "Size of bimap should be two")

actual.Delete(dummyKey)

fwdExpected := make(map[string]string)
invExpected := make(map[string]string)
fwdExpected[key] = value
invExpected[value] = key

expected := &BiMap[string, string]{forward: fwdExpected, backward: invExpected}

assert.Equal(t, 1, actual.Size(), "Size of bimap should be two")
assert.Equal(t, expected, actual, "They should be the same")

actual.Delete(dummyKey)

assert.Equal(t, 1, actual.Size(), "Size of bimap should be two")
assert.Equal(t, expected, actual, "They should be the same")
}

func TestBiMap_InverseDelete(t *testing.T) {
actual := NewBiMap[string, string]()
dummyKey := "DummyKey"
dummyVal := "DummyVal"
actual.Insert(key, value)
actual.Insert(dummyKey, dummyVal)

assert.Equal(t, 2, actual.Size(), "Size of bimap should be two")

actual.DeleteInverse(dummyVal)

fwdExpected := make(map[string]string)
invExpected := make(map[string]string)
fwdExpected[key] = value
invExpected[value] = key

expected := &BiMap[string, string]{forward: fwdExpected, backward: invExpected}

assert.Equal(t, 1, actual.Size(), "Size of bimap should be two")
assert.Equal(t, expected, actual, "They should be the same")

actual.DeleteInverse(dummyVal)

assert.Equal(t, 1, actual.Size(), "Size of bimap should be two")
assert.Equal(t, expected, actual, "They should be the same")
}

func TestBiMap_WithVaryingType(t *testing.T) {
actual := NewBiMap[string, int]()
dummyKey := "Dummy key"
dummyVal := 3

actual.Insert(dummyKey, dummyVal)

res, _ := actual.Get(dummyKey)
resVal, _ := actual.GetInverse(dummyVal)
assert.Equal(t, dummyVal, res, "Get by string key should return integer val")
assert.Equal(t, dummyKey, resVal, "Get by integer val should return string key")

}

0 comments on commit d4fc4c5

Please sign in to comment.