#region License Information /* HeuristicLab * Copyright (C) 2002-2019 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 { [StorableClass] [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 [StorableClass] 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(); } } } }