< Summary

Class:Towel.DataStructures.Bag
Assembly:Towel
File(s):File 1: /home/runner/work/Towel/Towel/Sources/Towel/DataStructures/Bag.cs
Covered lines:5
Uncovered lines:18
Coverable lines:23
Total lines:364
Line coverage:21.7% (5 of 23)
Covered branches:1
Total branches:12
Branch coverage:8.3% (1 of 12)

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
File 1: Get(...)25%471.42%
File 1: Set(...)0%40%
File 1: Counts(...)0%20%
File 1: Counts(...)100%10%
File 1: CountsBreak(...)0%20%

File(s)

/home/runner/work/Towel/Towel/Sources/Towel/DataStructures/Bag.cs

#LineLine coverage
 1namespace Towel.DataStructures;
 2
 3/// <summary>A bag between instances of two types. The polymorphism base for bag implementations in Towel.</summary>
 4/// <typeparam name="T">The generic type to be stored in this data structure.</typeparam>
 5public interface IBag<T> : IDataStructure<T>,
 6  DataStructure.ICountable,
 7  DataStructure.IClearable,
 8  DataStructure.IAuditable<T>,
 9  DataStructure.IRemovable<T>,
 10  DataStructure.IAddable<T>
 11{
 12  #region Properties
 13
 14  /// <summary>Allows indexed look-up of the structure. (Set does not replace the Add() method)</summary>
 15  /// <param name="countedValue">The "index" to access of the structure.</param>
 16  /// <returns>The value at the index of the requested key.</returns>
 17  int this[T countedValue] { get; set; }
 18
 19  #endregion
 20
 21  #region Methods
 22
 23  /// <summary>Tries to get the count of a value.</summary>
 24  /// <param name="value">The value to get the count of.</param>
 25  /// <returns>
 26  /// - <see cref="bool"/> Success: true if the key was found or false if not.<br/>
 27  /// - <see cref="Exception"/>? Exception: the exception that occured if the get failed.<br/>
 28  /// - <see cref="int"/>? Value: the value if the key was found or default if not.
 29  /// </returns>
 30  (bool Success, Exception? Exception, int? Count) TryGet(T value);
 31
 32  /// <summary>Sets value in the bag.</summary>
 33  /// <param name="value">The value to be set.</param>
 34  /// <param name="count">The number of values to set.</param>
 35  /// <returns>
 36  /// - <see cref="bool"/> Success: true if the key+value was set or false if not.<br/>
 37  /// - <see cref="Exception"/>? Exception: the exception that occured if the set failed.
 38  /// </returns>
 39  (bool Success, Exception? Exception, bool? Existed, int? OldCount) TrySet(T value, int count);
 40
 41  /// <summary>Tries to add a value to the bag.</summary>
 42  /// <param name="value">The value to be added.</param>
 43  /// <param name="count">The number of values to be added.</param>
 44  /// <returns>
 45  /// - <see cref="bool"/> Success: true if the values was added or false if not.<br/>
 46  /// - <see cref="Exception"/>? Exception: the exception that occured if the add failed.
 47  /// </returns>
 48  (bool Success, Exception? Exception) TryAdd(T value, int count);
 49
 50  (bool Success, Exception? Exception) DataStructure.IRemovable<T>.TryRemove(T value)
 51  {
 52    var (success, exception, _, _) = TryRemove(value);
 53    return (success, exception);
 54  }
 55
 56  /// <summary>Tries to remove a value from the bag.</summary>
 57  /// <param name="value">The value to remove from the bag.</param>
 58  /// <returns>
 59  /// - <see cref="bool"/> Success: true if the values were removed or false if not.<br/>
 60  /// - <see cref="Exception"/>? Exception: the exception that occured if the remove failed.<br/>
 61  /// - <see cref="int"/>? OldCount: the count of the value before the removal.<br/>
 62  /// - <see cref="int"/>? NewCount: the count of the value after the removal.
 63  /// </returns>
 64  new (bool Success, Exception? Exception, int? OldCount, int? NewCount) TryRemove(T value);
 65
 66  /// <summary>Tries to remove a value from the bag.</summary>
 67  /// <param name="value">The value to be removed.</param>
 68  /// <param name="count">The number of values to be removed.</param>
 69  /// <returns>
 70  /// - <see cref="bool"/> Success: true if the values were removed or false if not.<br/>
 71  /// - <see cref="Exception"/>? Exception: the exception that occured if the remove failed.<br/>
 72  /// - <see cref="int"/>? OldCount: the count of the value before the removal.<br/>
 73  /// - <see cref="int"/>? NewCount: the count of the value after the removal.
 74  /// </returns>
 75  (bool Success, Exception? Exception, int? OldCount, int? NewCount) TryRemove(T value, int count);
 76
 77  /// <summary>Gets an enumerator that will traverse the pairs of the bag.</summary>
 78  /// <returns>An enumerator that will traverse the pairs of the bag.</returns>
 79  System.Collections.Generic.IEnumerable<(int Count, T Value)> GetCounts();
 80
 81  /// <summary>Gets an array with all the pairs in the bag.</summary>
 82  /// <returns>An array with all the pairs in the bag.</returns>
 83  (int Count, T Value)[] CountsToArray();
 84
 85  /// <summary>Performs a function on every pair in a bag.</summary>
 86  /// <typeparam name="TStep">The type of the step function.</typeparam>
 87  /// <param name="step">The step function to perform on every pair.</param>
 88  /// <returns>The status of traversal.</returns>
 89  StepStatus CountsBreak<TStep>(TStep step = default)
 90    where TStep : struct, IFunc<(int Count, T Value), StepStatus>;
 91
 92  #endregion
 93}
 94
 95/// <summary>Static Extension class for bag interface implementers.</summary>
 96public static class Bag
 97{
 98  #region Extensions Methods
 99
 100  /// <summary>Gets the count of a <paramref name="value"/> in a <paramref name="bag"/>.</summary>
 101  /// <typeparam name="T">The generic type to be stored in this data structure.</typeparam>
 102  /// <param name="bag">The bag to get the count of a <paramref name="value"/> in.</param>
 103  /// <param name="value">The value to get the count of.</param>
 104  /// <returns>The count of the <paramref name="value"/>s in the <paramref name="bag"/>.</returns>
 105  public static int Get<T>(this IBag<T> bag, T value)
 12000106  {
 12000107    var (success, exception, count) = bag.TryGet(value);
 12000108    if (!success)
 0109    {
 0110      throw exception ?? new ArgumentException($"{nameof(Get)} failed but the {nameof(exception)} is null");
 111    }
 12000112    return count!.Value;
 12000113  }
 114
 115  /// <summary>Sets the <paramref name="count"/> of a <paramref name="value"/> in a <paramref name="bag"/>.</summary>
 116  /// <typeparam name="T">The generic type to be stored in this data structure.</typeparam>
 117  /// <param name="bag">The bag to set the value count in.</param>
 118  /// <param name="value">The value to set the <paramref name="count"/> of.</param>
 119  /// <param name="count">The count to set the number of <paramref name="value"/>'s to.</param>
 120  /// <returns>
 121  /// - <see cref="bool"/> Existed: True if the value already existed or false.<br/>
 122  /// - <see cref="int"/>? OldCount: The previous count if the value existed or default.
 123  /// </returns>
 124  public static (bool Existed, int? OldCount) Set<T>(this IBag<T> bag, T value, int count)
 0125  {
 0126    var (success, exception, existed, oldCount) = bag.TrySet(value, count);
 0127    if (!success)
 0128    {
 0129      throw exception ?? new ArgumentException($"{nameof(Get)} failed but the {nameof(exception)} is null");
 130    }
 0131    return (existed!.Value, oldCount);
 0132  }
 133
 134  /// <summary>Performs a function on every pair in a bag.</summary>
 135  /// <typeparam name="T">The type of values in the bag.</typeparam>
 136  /// <param name="bag">The bag to traverse the pairs of.</param>
 137  /// <param name="step">The step function to perform on every pair.</param>
 138  public static void Counts<T>(this IBag<T> bag, Action<(int Count, T Value)> step)
 0139  {
 0140    if (step is null) throw new ArgumentNullException(nameof(step));
 0141    bag.Counts<T, SAction<(int Count, T Value)>>(step);
 0142  }
 143
 144  /// <summary>Performs a function on every pair in a bag.</summary>
 145  /// <typeparam name="T">The type of values in the bag.</typeparam>
 146  /// <typeparam name="TStep">The type of step function to perform on every pair.</typeparam>
 147  /// <param name="bag">The bag to traverse the pairs of.</param>
 148  /// <param name="step">The step function to perform on every pair.</param>
 149  public static void Counts<T, TStep>(this IBag<T> bag, TStep step = default)
 150    where TStep : struct, IAction<(int Count, T Value)> =>
 0151    bag.CountsBreak<StepBreakFromAction<(int Count, T Value), TStep>>(step);
 152
 153  /// <summary>Performs a function on every pair in a bag.</summary>
 154  /// <typeparam name="T">The type of values in the bag.</typeparam>
 155  /// <param name="bag">The bag to traverse the pairs of.</param>
 156  /// <param name="step">The step function to perform on every pair.</param>
 157  /// <returns>The status of the traversal.</returns>
 158  public static StepStatus CountsBreak<T>(this IBag<T> bag, Func<(int Count, T Value), StepStatus> step)
 0159  {
 0160    if (step is null) throw new ArgumentNullException(nameof(step));
 0161    return bag.CountsBreak<SFunc<(int Count, T Value), StepStatus>>(step);
 0162  }
 163
 164  #endregion
 165}
 166
 167/// <summary>Static helpers.</summary>
 168public static class BagMap
 169{
 170  #region Extension Methods
 171
 172  /// <summary>Constructs a new <see cref="BagMap{T, TMap}"/>.</summary>
 173  /// <typeparam name="T">The type of values stored in this data structure.</typeparam>
 174  /// <param name="equate">The function for comparing <typeparamref name="T"/> values for equality.</param>
 175  /// <param name="hash">The function for hashing <typeparamref name="T"/> values.</param>
 176  /// <returns>The new constructed <see cref="BagMap{T, TMap}"/>.</returns>
 177  public static BagMap<T, MapHashLinked<int, T, SFunc<T, T, bool>, SFunc<T, int>>> New<T>(
 178    Func<T, T, bool>? equate = null,
 179    Func<T, int>? hash = null) =>
 180    new(new MapHashLinked<int, T, SFunc<T, T, bool>, SFunc<T, int>>(equate ?? Equate, hash ?? Hash));
 181
 182  /// <summary>Constructs a new <see cref="BagMap{T, TMap}"/>.</summary>
 183  /// <typeparam name="T">The type of values stored in this data structure.</typeparam>
 184  /// <param name="equate">The function for comparing <typeparamref name="T"/> values for equality.</param>
 185  /// <param name="hash">The function for hashing <typeparamref name="T"/> values.</param>
 186  /// <returns>The new constructed <see cref="BagMap{T, TMap}"/>.</returns>
 187  public static BagMap<T, MapHashLinked<int, T, SFunc<T, T, bool>, SFunc<T, int>>> NewHashLinked<T>(
 188    Func<T, T, bool>? equate = null,
 189    Func<T, int>? hash = null) =>
 190    new(new MapHashLinked<int, T, SFunc<T, T, bool>, SFunc<T, int>>(equate ?? Equate, hash ?? Hash));
 191
 192  #endregion
 193}
 194
 195/// <summary>An unsorted structure of unique items.</summary>
 196/// <typeparam name="T">The generic type of the structure.</typeparam>
 197/// <typeparam name="TMap">The type of function for quality checking <typeparamref name="T"/> values.</typeparam>
 198public class BagMap<T, TMap> : IBag<T>,
 199  ICloneable<BagMap<T, TMap>>
 200  where TMap : IMap<int, T>, ICloneable<TMap>
 201{
 202  internal TMap _map;
 203  internal int _count;
 204
 205  #region Constructors
 206
 207  internal BagMap(TMap map)
 208  {
 209    _map = map;
 210    _count = 0;
 211  }
 212
 213  /// <summary>This constructor is for cloning purposes.</summary>
 214  /// <param name="bag">The bag to clone.</param>
 215  internal BagMap(BagMap<T, TMap> bag)
 216  {
 217    _map = bag._map.Clone();
 218    _count = bag._count;
 219  }
 220
 221  #endregion
 222
 223  #region Properties
 224
 225  /// <inheritdoc/>
 226  public int Count => _count;
 227
 228  /// <inheritdoc/>
 229  public int this[T countedValue]
 230  {
 231    get => this.Get(countedValue);
 232    set => this.Set(countedValue, value);
 233  }
 234
 235  #endregion
 236
 237  #region Methods
 238
 239  /// <inheritdoc/>
 240  public (bool Success, Exception? Exception) TryAdd(T value) => TryAdd(value, 1);
 241
 242  /// <inheritdoc/>
 243  public (bool Success, Exception? Exception) TryAdd(T value, int count)
 244  {
 245    var (success, exception, _, _) = _map.TryAddOrUpdate<Int32Add>(value, count, count);
 246    if (success)
 247    {
 248      _count += count;
 249    }
 250    return (success, exception);
 251  }
 252
 253  /// <inheritdoc/>
 254  public (bool Success, Exception? Exception, int? Count) TryGet(T value)
 255  {
 256    var (success, _, count) = _map.TryGet(value);
 257    return (true, null, success ? count : 0);
 258  }
 259
 260  /// <inheritdoc/>
 261  public (bool Success, Exception? Exception, bool? Existed, int? OldCount) TrySet(T value, int count)
 262  {
 263    if (count < 0)
 264    {
 265      return (false, new ArgumentOutOfRangeException(paramName: nameof(count), message: $"{nameof(count)} < 0", actualVa
 266    }
 267    var (success, exception, existed, oldCount) = _map.TrySet(value, count);
 268    if (success)
 269    {
 270      if (existed!.Value)
 271      {
 272        _count -= oldCount;
 273      }
 274      _count += count;
 275    }
 276    return (success, exception, existed, oldCount);
 277  }
 278
 279  /// <inheritdoc/>
 280  public (bool Success, Exception? Exception, int? OldCount, int? NewCount) TryRemove(T value) => TryRemove(value, 1);
 281
 282  /// <inheritdoc/>
 283  public (bool Success, Exception? Exception, int? OldCount, int? NewCount) TryRemove(T value, int count)
 284  {
 285#warning TODO: optimize by injecting failure check in the TMap
 286    var (getSuccess, getException, oldCount) = _map.TryGet(value);
 287    if (!getSuccess)
 288    {
 289      return (false, new ArgumentException(message: "removal failed", innerException: getException), default, default);
 290    }
 291    if (count > oldCount)
 292    {
 293      return (false, new ArgumentException(message: "attempting to remove non-existing values from a bag"), default, def
 294    }
 295    int newCount = oldCount - count;
 296    var (setSuccess, setException, _, _) = _map.TrySet(value, newCount);
 297    if (!setSuccess)
 298    {
 299      return (false, setException, default, default);
 300    }
 301    _count -= count;
 302    return (true, null, oldCount, newCount);
 303  }
 304
 305  /// <inheritdoc/>
 306  public BagMap<T, TMap> Clone() => new(this);
 307
 308  /// <inheritdoc/>
 309  public bool Contains(T value) => _map.Contains(value);
 310
 311  /// <inheritdoc/>
 312  public void Clear()
 313  {
 314    _map.Clear();
 315    _count = 0;
 316  }
 317
 318  /// <inheritdoc/>
 319  public StepStatus StepperBreak<TStep>(TStep step = default)
 320    where TStep : struct, IFunc<T, StepStatus> =>
 321    _map.KeysBreak(step);
 322
 323  /// <inheritdoc/>
 324  public StepStatus CountsBreak<TStep>(TStep step = default)
 325    where TStep : struct, IFunc<(int Count, T Value), StepStatus> =>
 326    _map.PairsBreak(step);
 327
 328  /// <inheritdoc/>
 329  public System.Collections.Generic.IEnumerable<(int Count, T Value)> GetCounts() => _map.GetPairs();
 330
 331  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
 332
 333  /// <inheritdoc/>
 334  public System.Collections.Generic.IEnumerator<T> GetEnumerator()
 335  {
 336    foreach (var (count, value) in _map.GetPairs())
 337    {
 338      for (int i = 0; i < count; i++)
 339      {
 340        yield return value;
 341      }
 342    }
 343  }
 344
 345  /// <inheritdoc/>
 346  public T[] ToArray()
 347  {
 348    T[] array = new T[_count];
 349    int index = 0;
 350    foreach (var (count, value) in _map.GetPairs())
 351    {
 352      for (int i = 0; i < count; i++)
 353      {
 354        array[index++] = value;
 355      }
 356    }
 357    return array;
 358  }
 359
 360  /// <inheritdoc/>
 361  public (int Count, T Value)[] CountsToArray() => _map.PairsToArray();
 362
 363  #endregion
 364}