Skip to content

Commit d2e50fc

Browse files
author
Oren (electricessence)
committed
Updates to Triangular math.
1 parent 41c15f0 commit d2e50fc

File tree

3 files changed

+200
-18
lines changed

3 files changed

+200
-18
lines changed

Open.Arithmetic.csproj

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@ Part of the "Open" set of libraries.</Description>
1414
<PackageProjectUrl>https://github.com/electricessence/Open.Arithmetic/</PackageProjectUrl>
1515
<RepositoryUrl>https://github.com/electricessence/Open.Arithmetic/</RepositoryUrl>
1616
<RepositoryType>git</RepositoryType>
17-
<PackageTags>dotnet, dotnet-core, dotnetcore, cs, math, dynamic, arithmetic</PackageTags>
18-
<Version>1.1.0</Version>
19-
<PackageReleaseNotes>Updated to .NET Standard for compatability.</PackageReleaseNotes>
20-
<AssemblyVersion>1.1.0.0</AssemblyVersion>
21-
<FileVersion>1.1.0.0</FileVersion>
17+
<PackageTags>dotnet, dotnet-core, dotnetcore, cs, math, dynamic, arithmetic, triangular, statistical, variance, covariance, correlation</PackageTags>
18+
<Version>1.1.1</Version>
19+
<PackageReleaseNotes>Extends Triangular methods and added TriangularIndexer.</PackageReleaseNotes>
20+
<AssemblyVersion>1.1.1.0</AssemblyVersion>
21+
<FileVersion>1.1.1.0</FileVersion>
22+
</PropertyGroup>
23+
24+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
25+
<LangVersion>latest</LangVersion>
26+
</PropertyGroup>
27+
28+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
29+
<LangVersion>latest</LangVersion>
2230
</PropertyGroup>
2331

2432
<ItemGroup>

Triangular.cs

Lines changed: 135 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,175 @@
66
using Open.Collections;
77
using System;
88
using System.Collections.Generic;
9+
using System.Diagnostics.Contracts;
910
using System.Linq;
10-
using System.Threading;
1111

1212
namespace Open.Arithmetic
1313
{
1414

1515
public static class Triangular
1616
{
17+
public static readonly short MaxInt16 = Reverse(short.MaxValue);
18+
public static readonly ushort MaxUInt16 = Reverse(ushort.MaxValue);
19+
public static readonly int MaxInt32 = Reverse(int.MaxValue);
20+
public static readonly uint MaxUInt32 = Reverse(uint.MaxValue);
21+
public static readonly long MaxInt64 = Reverse(long.MaxValue);
22+
public static readonly ulong MaxUInt64 = Reverse(ulong.MaxValue);
1723

18-
public static uint Forward(uint n)
24+
/// <summary>
25+
/// Returns the total count of triangular numbers bound by n.
26+
/// </summary>
27+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
28+
/// <exception cref="ArgumentOutOfRangeException">Result will exceed maximum value for a 32 bit integer.</exception>
29+
public static int ForwardInt(int n)
1930
{
31+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
32+
if (n > MaxInt32) throw new ArgumentOutOfRangeException(nameof(n), n, "Result will exceed maximum value for a 32 bit integer.");
33+
Contract.EndContractBlock();
34+
35+
return (int)Forward(n);
36+
}
37+
38+
/// <summary>
39+
/// Returns the total count of triangular numbers bound by n.
40+
/// </summary>
41+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
42+
/// <exception cref="ArgumentOutOfRangeException">Result will exceed maximum value for an unsigned 32 bit integer.</exception>
43+
public static uint ForwardInt(uint n)
44+
{
45+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
46+
if (n > MaxUInt32) throw new ArgumentOutOfRangeException(nameof(n), n, "Result will exceed maximum value for an unsigned 32 bit integer.");
47+
Contract.EndContractBlock();
48+
49+
return (uint)Forward(n);
50+
}
51+
52+
/// <summary>
53+
/// Returns the total count of triangular numbers bound by n.
54+
/// </summary>
55+
public static ulong Forward(long n)
56+
{
57+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
58+
Contract.EndContractBlock();
59+
60+
return Forward((ulong)n);
61+
}
62+
63+
/// <summary>
64+
/// Returns the total count of triangular numbers bound by n.
65+
/// </summary>
66+
/// <exception cref="ArgumentOutOfRangeException">Result will exceed maximum value for an unsigned 64 bit integer.</exception>
67+
public static ulong Forward(ulong n)
68+
{
69+
if (n > MaxUInt64) throw new ArgumentOutOfRangeException(nameof(n), n, "Result will exceed maximum value for an unsigned 64 bit integer.");
70+
Contract.EndContractBlock();
71+
2072
return n * (n + 1) / 2;
2173
}
2274

75+
static double ReverseInternal(double n)
76+
=> (Math.Sqrt(8 * n + 1) - 1) / 2;
77+
78+
/// <summary>
79+
/// Returns the source value (possible decimal) from an index in a triangular set.
80+
/// </summary>
81+
/// <exception cref="ArgumentOutOfRangeException">Cannot be NaN.</exception>
82+
/// <exception cref="ArgumentOutOfRangeException">Must be finite.</exception>
83+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
84+
public static double Reverse(double n)
85+
{
86+
if (double.IsNaN(n)) throw new ArgumentOutOfRangeException(nameof(n), n, "Cannot be NaN.");
87+
if (double.IsInfinity(n)) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be finite.");
88+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
89+
Contract.EndContractBlock();
90+
91+
return ReverseInternal(n);
92+
}
93+
94+
/// <summary>
95+
/// Returns the source index from an index in a triangular set.
96+
/// </summary>
97+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
98+
public static ulong Reverse(ulong n)
99+
=> (ulong)ReverseInternal(n);
23100

101+
/// <summary>
102+
/// Returns the source index from an index in a triangular set.
103+
/// </summary>
24104
public static uint Reverse(uint n)
105+
=> (uint)ReverseInternal(n);
106+
107+
/// <summary>
108+
/// Returns the source index from an index in a triangular set.
109+
/// </summary>
110+
public static short Reverse(short n)
111+
=> (short)ReverseInternal(n);
112+
113+
/// <summary>
114+
/// Returns the source index from an index in a triangular set.
115+
/// </summary>
116+
public static ushort Reverse(ushort n)
117+
=> (ushort)ReverseInternal(n);
118+
119+
/// <summary>
120+
/// Returns the source index from an index in a triangular set.
121+
/// </summary>
122+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
123+
public static long Reverse(long n)
25124
{
26-
return (uint)(Math.Sqrt(8 * n + 1) - 1) / 2;
125+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
126+
Contract.EndContractBlock();
127+
128+
return (long)ReverseInternal(n);
129+
}
130+
131+
/// <summary>
132+
/// Returns the source index from an index in a triangular set.
133+
/// </summary>
134+
/// <exception cref="ArgumentOutOfRangeException">Must be at least zero.</exception>
135+
public static int Reverse(int n)
136+
{
137+
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), n, "Must be at least zero.");
138+
Contract.EndContractBlock();
139+
140+
return (int)ReverseInternal(n);
27141
}
28142

29143
public static class Disperse
30144
{
31145

32146
/**
33-
* Increases the number an element based on it's index.
147+
* Increases the repeat of an element based on its index.
148+
* 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, ...
34149
* @param source
35150
* @returns {Enumerable<T>}
36151
*/
37152
public static IEnumerable<T> Increasing<T>(IEnumerable<T> source)
38-
{
39-
int i = 0;
40-
return source.SelectMany(
41-
c => Enumerable.Repeat(c, Interlocked.Increment(ref i)));
42-
}
153+
=> source.SelectMany((c, i) => Enumerable.Repeat(c, i));
43154

44155
/**
45-
* Increases the count of each element for each index.
156+
* Starting with the first item, increases the size of the loop of items until there is no more.
157+
* The quantity/total of an item is lessened as the loop spreads the items out as it progresses.
158+
* Given an entire set, the first item is represented the most and the last item is represented the least.
159+
* 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, ...
46160
* @param source
47161
* @returns {Enumerable<T>}
48162
*/
49163
public static IEnumerable<T> Decreasing<T>(IEnumerable<T> source)
50164
{
51165
var s = source.Memoize();
52-
int i = 0;
53-
return s.SelectMany(
54-
c => s.Take(Interlocked.Increment(ref i)));
166+
return s.SelectMany((c, i) => s.Take(i));
55167
}
168+
169+
/**
170+
* Effectively the same as Disperse.Increasing but in reverse.
171+
* Given a fininte set, calling Disperse.Increasing(sourse).Reverse() could produce the desired result as well.
172+
* 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5
173+
* @param source
174+
* @returns {Enumerable<T>}
175+
*/
176+
public static IEnumerable<T> Descending<T>(IReadOnlyList<T> source)
177+
=> source.SelectMany((c, i) => Enumerable.Repeat(c, source.Count - i));
56178
}
57179
}
58180

TriangularIndexer.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Diagnostics.Contracts;
5+
6+
namespace Open.Arithmetic
7+
{
8+
public class TriangularIndexer<T> : IReadOnlyList<T>
9+
{
10+
public readonly IReadOnlyList<T> Source;
11+
public readonly bool IsDescending;
12+
13+
public TriangularIndexer(IReadOnlyList<T> source, bool descending = false)
14+
{
15+
Source = source ?? throw new ArgumentNullException(nameof(source));
16+
if (source.Count > Triangular.MaxInt32)
17+
throw new ArgumentException($"Source collection is too large to be indexed. Max size: {Triangular.MaxInt32}", nameof(source));
18+
Contract.EndContractBlock();
19+
20+
IsDescending = descending;
21+
}
22+
23+
public T this[int index]
24+
=> IsDescending
25+
? Source[Source.Count - Triangular.Reverse(Count - index)]
26+
: Source[Triangular.Reverse(index)];
27+
28+
public int Count
29+
=> Triangular.ForwardInt(Source.Count);
30+
31+
public IEnumerator<T> GetEnumerator()
32+
{
33+
var source = IsDescending
34+
? Triangular.Disperse.Descending(Source)
35+
: Triangular.Disperse.Increasing(Source);
36+
37+
foreach (var e in source)
38+
yield return e;
39+
}
40+
41+
IEnumerator IEnumerable.GetEnumerator()
42+
=> GetEnumerator();
43+
}
44+
45+
public static class TriangularIndexer
46+
{
47+
public static TriangularIndexer<T> Increasing<T>(IReadOnlyList<T> source)
48+
=> new TriangularIndexer<T>(source);
49+
public static TriangularIndexer<T> Decreasing<T>(IReadOnlyList<T> source)
50+
=> new TriangularIndexer<T>(source, true);
51+
}
52+
}

0 commit comments

Comments
 (0)