#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Collections {
[StorableType("0BF45891-847C-428B-9F2C-FD682320066C")]
[Serializable]
public class BidirectionalLookup {
[Storable]
private readonly Dictionary> firstToSecond;
[Storable]
private readonly Dictionary> secondToFirst;
[StorableConstructor]
protected BidirectionalLookup(bool deserializing) : base() { }
public BidirectionalLookup() {
firstToSecond = new Dictionary>();
secondToFirst = new Dictionary>();
}
public BidirectionalLookup(IEqualityComparer firstComparer) {
firstToSecond = new Dictionary>(firstComparer);
secondToFirst = new Dictionary>();
}
public BidirectionalLookup(IEqualityComparer secondComparer) {
firstToSecond = new Dictionary>();
secondToFirst = new Dictionary>(secondComparer);
}
public BidirectionalLookup(IEqualityComparer firstComparer, IEqualityComparer secondComparer) {
firstToSecond = new Dictionary>(firstComparer);
secondToFirst = new Dictionary>(secondComparer);
}
#region Properties
public int CountFirst {
get { return firstToSecond.Count; }
}
public int CountSecond {
get { return secondToFirst.Count; }
}
public IEnumerable FirstKeys {
get { return firstToSecond.Keys.AsEnumerable(); }
}
public IEnumerable SecondKeys {
get { return secondToFirst.Keys.AsEnumerable(); }
}
public IEnumerable> FirstEnumerable {
get { return firstToSecond.Select(x => new StorableGrouping(x.Key, x.Value, secondToFirst.Comparer)); }
}
public IEnumerable> SecondEnumerable {
get { return secondToFirst.Select(x => new StorableGrouping(x.Key, x.Value, firstToSecond.Comparer)); }
}
#endregion
#region Methods
public void Add(TFirst firstValue, TSecond secondValue) {
HashSet firstSet;
if (!firstToSecond.TryGetValue(firstValue, out firstSet)) {
firstSet = new HashSet(secondToFirst.Comparer);
firstToSecond[firstValue] = firstSet;
}
HashSet secondSet;
if (!secondToFirst.TryGetValue(secondValue, out secondSet)) {
secondSet = new HashSet(firstToSecond.Comparer);
secondToFirst[secondValue] = secondSet;
}
firstSet.Add(secondValue);
secondSet.Add(firstValue);
}
public void AddRangeFirst(TFirst firstValue, IEnumerable secondValues) {
HashSet firstSet;
if (!firstToSecond.TryGetValue(firstValue, out firstSet)) {
firstSet = new HashSet(secondToFirst.Comparer);
firstToSecond[firstValue] = firstSet;
}
foreach (var s in secondValues) {
HashSet secondSet;
if (!secondToFirst.TryGetValue(s, out secondSet)) {
secondSet = new HashSet(firstToSecond.Comparer);
secondToFirst[s] = secondSet;
}
firstSet.Add(s);
secondSet.Add(firstValue);
}
}
public void AddRangeSecond(TSecond secondValue, IEnumerable firstValues) {
HashSet secondSet;
if (!secondToFirst.TryGetValue(secondValue, out secondSet)) {
secondSet = new HashSet(firstToSecond.Comparer);
secondToFirst[secondValue] = secondSet;
}
foreach (var f in firstValues) {
HashSet firstSet;
if (!firstToSecond.TryGetValue(f, out firstSet)) {
firstSet = new HashSet(secondToFirst.Comparer);
firstToSecond[f] = firstSet;
}
firstSet.Add(secondValue);
secondSet.Add(f);
}
}
public bool ContainsFirst(TFirst firstValue) {
return firstToSecond.ContainsKey(firstValue);
}
public bool ContainsSecond(TSecond secondValue) {
return secondToFirst.ContainsKey(secondValue);
}
public IEnumerable GetByFirst(TFirst firstValue) {
return firstToSecond[firstValue];
}
public IEnumerable GetBySecond(TSecond secondValue) {
return secondToFirst[secondValue];
}
public void SetByFirst(TFirst firstValue, IEnumerable secondValues) {
RemoveByFirst(firstValue);
AddRangeFirst(firstValue, secondValues);
}
public void SetBySecond(TSecond secondValue, IEnumerable firstValues) {
RemoveBySecond(secondValue);
AddRangeSecond(secondValue, firstValues);
}
public void RemovePair(TFirst first, TSecond second) {
if (!ContainsFirst(first) || !ContainsSecond(second)) return;
firstToSecond[first].Remove(second);
if (!firstToSecond[first].Any()) firstToSecond.Remove(first);
secondToFirst[second].Remove(first);
if (!secondToFirst[second].Any()) secondToFirst.Remove(second);
}
public void RemoveByFirst(TFirst firstValue) {
if (!ContainsFirst(firstValue)) return;
var secondValues = firstToSecond[firstValue].ToArray();
firstToSecond.Remove(firstValue);
foreach (var s in secondValues) {
secondToFirst[s].Remove(firstValue);
if (!secondToFirst[s].Any()) secondToFirst.Remove(s);
}
}
public void RemoveBySecond(TSecond secondValue) {
if (!ContainsSecond(secondValue)) return;
var firstValues = secondToFirst[secondValue].ToArray();
secondToFirst.Remove(secondValue);
foreach (var f in firstValues) {
firstToSecond[f].Remove(secondValue);
if (!firstToSecond[f].Any()) firstToSecond.Remove(f);
}
}
public void Clear() {
firstToSecond.Clear();
secondToFirst.Clear();
}
#endregion
[StorableType("066A8A8B-784D-45CC-9063-F2FC8E9A4442")]
private class StorableGrouping : IGrouping {
[Storable]
private readonly TKey key;
[Storable]
private readonly HashSet values;
public StorableGrouping(TKey key, IEnumerable values, IEqualityComparer comparer) {
this.key = key;
this.values = new HashSet(values, comparer);
}
public TKey Key {
get { return key; }
}
public IEnumerator GetEnumerator() {
return values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
}
}