Skip to content

Commit

Permalink
Additional Arities (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
wbaldoumas authored May 15, 2023
1 parent 364440b commit 41968af
Show file tree
Hide file tree
Showing 8 changed files with 729 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.0] 2023-05-14

### Added

Support for third, fourth, and fifth Cartesian product dimensions.

## [0.2.0] 2023-05-14

### Added
Expand Down
85 changes: 85 additions & 0 deletions src/LazyCart/ILazyCartesianProduct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,89 @@ public interface ILazyCartesianProduct<T1, T2, T3> : ILazyCartesianProduct
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="sampleSize"/> is greater than <see cref="ILazyCartesianProduct.Size"/>.</exception>
/// <returns>An evenly-distributed, random sample of Cartesian product entries.</returns>
IEnumerable<(T1, T2, T3)> GenerateSamples(BigInteger sampleSize);
}

/// <summary>
/// Represents a Cartesian product of four sets.
/// </summary>
/// <typeparam name="T1">The type of the first set.</typeparam>
/// <typeparam name="T2">The type of the second set.</typeparam>
/// <typeparam name="T3">The type of the third set.</typeparam>
/// <typeparam name="T4">The type of the fourth set.</typeparam>
public interface ILazyCartesianProduct<T1, T2, T3, T4> : ILazyCartesianProduct
{
/// <summary>
/// Returns the Cartesian product entry at the given index.
/// </summary>
/// <param name="index">The index of the Cartesian product entry to return.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
/// <returns>The Cartesian product entry at the given index.</returns>
(T1, T2, T3, T4) this[BigInteger index] { get; }

/// <summary>
/// Returns the Cartesian product entry at the given index.
/// </summary>
/// <param name="index">The index of the Cartesian product entry to return.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
/// <returns>The Cartesian product entry at the given index.</returns>
(T1, T2, T3, T4) AtIndex(BigInteger index);

/// <summary>
/// Returns the index of the given Cartesian product entry.
/// </summary>
/// <param name="entry">The Cartesian product entry to find the index of.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the Cartesian product entry is not found.</exception>
/// <returns>The index of the given Cartesian product entry.</returns>
BigInteger IndexOf((T1, T2, T3, T4) entry);

/// <summary>
/// Generates an evenly-distributed, random sample of Cartesian product entries.
/// </summary>
/// <param name="sampleSize">The number of samples to generate.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="sampleSize"/> is greater than <see cref="ILazyCartesianProduct.Size"/>.</exception>
/// <returns>An evenly-distributed, random sample of Cartesian product entries.</returns>
IEnumerable<(T1, T2, T3, T4)> GenerateSamples(BigInteger sampleSize);
}

/// <summary>
/// Represents a Cartesian product of five sets.
/// </summary>
/// <typeparam name="T1">The type of the first set.</typeparam>
/// <typeparam name="T2">The type of the second set.</typeparam>
/// <typeparam name="T3">The type of the third set.</typeparam>
/// <typeparam name="T4">The type of the fourth set.</typeparam>
/// <typeparam name="T5">The type of the fifth set.</typeparam>
public interface ILazyCartesianProduct<T1, T2, T3, T4, T5> : ILazyCartesianProduct
{
/// <summary>
/// Returns the Cartesian product entry at the given index.
/// </summary>
/// <param name="index">The index of the Cartesian product entry to return.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
/// <returns>The Cartesian product entry at the given index.</returns>
(T1, T2, T3, T4, T5) this[BigInteger index] { get; }

/// <summary>
/// Returns the Cartesian product entry at the given index.
/// </summary>
/// <param name="index">The index of the Cartesian product entry to return.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the index is out of range.</exception>
/// <returns>The Cartesian product entry at the given index.</returns>
(T1, T2, T3, T4, T5) AtIndex(BigInteger index);

/// <summary>
/// Returns the index of the given Cartesian product entry.
/// </summary>
/// <param name="entry">The Cartesian product entry to find the index of.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the Cartesian product entry is not found.</exception>
/// <returns>The index of the given Cartesian product entry.</returns>
BigInteger IndexOf((T1, T2, T3, T4, T5) entry);

/// <summary>
/// Generates an evenly-distributed, random sample of Cartesian product entries.
/// </summary>
/// <param name="sampleSize">The number of samples to generate.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="sampleSize"/> is greater than <see cref="ILazyCartesianProduct.Size"/>.</exception>
/// <returns>An evenly-distributed, random sample of Cartesian product entries.</returns>
IEnumerable<(T1, T2, T3, T4, T5)> GenerateSamples(BigInteger sampleSize);
}
2 changes: 1 addition & 1 deletion src/LazyCart/LazyCart.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>0.2.0</Version>
<Version>0.3.0</Version>
<Authors>William Baldoumas</Authors>
<Description>A tiny library to lazily generate the Nth cartesian product.</Description>
<Copyright>Copyright ©2023 William Baldoumas</Copyright>
Expand Down
215 changes: 215 additions & 0 deletions src/LazyCart/LazyCartesianProduct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,221 @@ private void Precompute()

factor *= _set2.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set1.Count));
}
}

/// <inheritdoc cref="ILazyCartesianProduct{T1,T2,T3,T4}"/>
public sealed class LazyCartesianProduct<T1, T2, T3, T4> : LazyCartesianProduct, ILazyCartesianProduct<T1, T2, T3, T4>
{
private readonly IList<T1> _set1;
private readonly IList<T2> _set2;
private readonly IList<T3> _set3;
private readonly IList<T4> _set4;
private readonly IList<BigInteger> _dividends;
private readonly IList<BigInteger> _moduli;

public LazyCartesianProduct(IList<T1> set1, IList<T2> set2, IList<T3> set3, IList<T4> set4)
{
_set1 = set1;
_set2 = set2;
_set3 = set3;
_set4 = set4;
_dividends = new List<BigInteger>();
_moduli = new List<BigInteger>();
Size = BigInteger.One;

Precompute();
}

public (T1, T2, T3, T4) this[BigInteger index] => AtIndex(index);

public (T1, T2, T3, T4) AtIndex(BigInteger index)
{
ValidateAtIndex(index, Size);

var item1 = CalculateItem(index, _dividends[0], _moduli[0], _set1);
var item2 = CalculateItem(index, _dividends[1], _moduli[1], _set2);
var item3 = CalculateItem(index, _dividends[2], _moduli[2], _set3);
var item4 = CalculateItem(index, _dividends[3], _moduli[3], _set4);

return (item1, item2, item3, item4);
}

public BigInteger IndexOf((T1, T2, T3, T4) entry)
{
var index1 = _set1.IndexOf(entry.Item1);
var index2 = _set2.IndexOf(entry.Item2);
var index3 = _set3.IndexOf(entry.Item3);
var index4 = _set4.IndexOf(entry.Item4);

ValidateIndexOf(nameof(entry), index1, index2, index3, index4);

return index1 * _dividends[0] + index2 * _dividends[1] + index3 * _dividends[2] + index4 * _dividends[3];
}

public IEnumerable<(T1, T2, T3, T4)> GenerateSamples(BigInteger sampleSize)
{
ValidateSampleSize(sampleSize, Size);

return GenerateSamplesInternal(sampleSize);
}

private IEnumerable<(T1, T2, T3, T4)> GenerateSamplesInternal(BigInteger sampleSize)
{
var sampledIndices = new HashSet<BigInteger>();

while (sampledIndices.Count < (int)sampleSize)
{
var randomIndex = ThreadLocalRandom.NextBigInteger(0, Size);

if (sampledIndices.Add(randomIndex))
{
yield return this[randomIndex];
}
}
}

private void Precompute()
{
Size *= _set1.Count;
Size *= _set2.Count;
Size *= _set3.Count;
Size *= _set4.Count;

var factor = BigInteger.One;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set4.Count));

factor *= _set4.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set3.Count));

factor *= _set3.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set2.Count));

factor *= _set2.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set1.Count));
}
}

public sealed class LazyCartesianProduct<T1, T2, T3, T4, T5> :
LazyCartesianProduct,
ILazyCartesianProduct<T1, T2, T3, T4, T5>
{
private readonly IList<T1> _set1;
private readonly IList<T2> _set2;
private readonly IList<T3> _set3;
private readonly IList<T4> _set4;
private readonly IList<T5> _set5;
private readonly IList<BigInteger> _dividends;
private readonly IList<BigInteger> _moduli;

public LazyCartesianProduct(IList<T1> set1, IList<T2> set2, IList<T3> set3, IList<T4> set4, IList<T5> set5)
{
_set1 = set1;
_set2 = set2;
_set3 = set3;
_set4 = set4;
_set5 = set5;
_dividends = new List<BigInteger>();
_moduli = new List<BigInteger>();
Size = BigInteger.One;

Precompute();
}

public (T1, T2, T3, T4, T5) this[BigInteger index] => AtIndex(index);

public (T1, T2, T3, T4, T5) AtIndex(BigInteger index)
{
ValidateAtIndex(index, Size);

var item1 = CalculateItem(index, _dividends[0], _moduli[0], _set1);
var item2 = CalculateItem(index, _dividends[1], _moduli[1], _set2);
var item3 = CalculateItem(index, _dividends[2], _moduli[2], _set3);
var item4 = CalculateItem(index, _dividends[3], _moduli[3], _set4);
var item5 = CalculateItem(index, _dividends[4], _moduli[4], _set5);

return (item1, item2, item3, item4, item5);
}

public BigInteger IndexOf((T1, T2, T3, T4, T5) entry)
{
var index1 = _set1.IndexOf(entry.Item1);
var index2 = _set2.IndexOf(entry.Item2);
var index3 = _set3.IndexOf(entry.Item3);
var index4 = _set4.IndexOf(entry.Item4);
var index5 = _set5.IndexOf(entry.Item5);

ValidateIndexOf(nameof(entry), index1, index2, index3, index4, index5);

return index1 * _dividends[0] +
index2 * _dividends[1] +
index3 * _dividends[2] +
index4 * _dividends[3] +
index5 * _dividends[4];
}

public IEnumerable<(T1, T2, T3, T4, T5)> GenerateSamples(BigInteger sampleSize)
{
ValidateSampleSize(sampleSize, Size);

return GenerateSamplesInternal(sampleSize);
}

private IEnumerable<(T1, T2, T3, T4, T5)> GenerateSamplesInternal(BigInteger sampleSize)
{
var sampledIndices = new HashSet<BigInteger>();

while (sampledIndices.Count < (int)sampleSize)
{
var randomIndex = ThreadLocalRandom.NextBigInteger(0, Size);

if (sampledIndices.Add(randomIndex))
{
yield return this[randomIndex];
}
}
}

private void Precompute()
{
Size *= _set1.Count;
Size *= _set2.Count;
Size *= _set3.Count;
Size *= _set4.Count;
Size *= _set5.Count;

var factor = BigInteger.One;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set5.Count));

factor *= _set5.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set4.Count));

factor *= _set4.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set3.Count));

factor *= _set3.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set2.Count));

factor *= _set2.Count;

_dividends.Insert(0, factor);
_moduli.Insert(0, new BigInteger(_set1.Count));
}
Expand Down
Loading

0 comments on commit 41968af

Please sign in to comment.