-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
RationalValue.cs
383 lines (328 loc) · 16.6 KB
/
RationalValue.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
// Copyright (c) Dmytro Kyshchenko. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics.CodeAnalysis;
namespace xFunc.Maths.Expressions;
/// <summary>
/// Represents the rational number.
/// </summary>
public readonly struct RationalValue : IEquatable<RationalValue>, IComparable<RationalValue>, IComparable
{
/// <summary>
/// Initializes a new instance of the <see cref="RationalValue"/> struct.
/// </summary>
/// <param name="numerator">The numerator.</param>
/// <param name="denominator">The denominator.</param>
public RationalValue(NumberValue numerator, NumberValue denominator)
{
Numerator = numerator;
Denominator = denominator;
}
/// <summary>
/// Initializes a new instance of the <see cref="RationalValue"/> struct.
/// </summary>
/// <param name="numerator">The numerator.</param>
/// <param name="denominator">The denominator.</param>
public RationalValue(double numerator, double denominator)
: this(new NumberValue(numerator), new NumberValue(denominator))
{
}
/// <summary>
/// Determines whether two specified instances of <see cref="RationalValue"/> are equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if <paramref name="left"/> is equal to <paramref name="right"/>; otherwise, <c>false</c>.</returns>
public static bool operator ==(RationalValue left, RationalValue right)
=> left.Equals(right);
/// <summary>
/// Determines whether two specified instances of <see cref="RationalValue"/> are not equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if <paramref name="left"/> is not equal to <paramref name="right"/>; otherwise, <c>false</c>.</returns>
public static bool operator !=(RationalValue left, RationalValue right)
=> !left.Equals(right);
/// <summary>
/// Indicates whether <paramref name="left"/> parameter is less than the <paramref name="right"/> parameter.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if the <paramref name="left"/> parameter is less than the <paramref name="right"/> parameter; otherwise, <c>false</c>.</returns>
public static bool operator <(RationalValue left, RationalValue right)
=> left.CompareTo(right) < 0;
/// <summary>
/// Indicates whether <paramref name="left"/> parameter is greater than the <paramref name="right"/> parameter.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if the <paramref name="left"/> parameter is greater than the <paramref name="right"/> parameter; otherwise, <c>false</c>.</returns>
public static bool operator >(RationalValue left, RationalValue right)
=> left.CompareTo(right) > 0;
/// <summary>
/// Indicates whether <paramref name="left"/> parameter is less than or equal to the <paramref name="right"/> parameter.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if the <paramref name="left"/> parameter is less than or equal to the <paramref name="right"/> parameter; otherwise, <c>false</c>.</returns>
public static bool operator <=(RationalValue left, RationalValue right)
=> left.CompareTo(right) <= 0;
/// <summary>
/// Indicates whether <paramref name="left"/> parameter is greater than or equal to the <paramref name="right"/> parameter.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns><c>true</c> if the <paramref name="left"/> parameter is greater than or equal to the <paramref name="right"/> parameter; otherwise, <c>false</c>.</returns>
public static bool operator >=(RationalValue left, RationalValue right)
=> left.CompareTo(right) >= 0;
/// <summary>
/// Produces the negative of <see cref="RationalValue"/>.
/// </summary>
/// <param name="rationalValue">The rational number.</param>
/// <returns>The negative of <paramref name="rationalValue"/>.</returns>
public static RationalValue operator -(RationalValue rationalValue)
=> new RationalValue(-rationalValue.Numerator, rationalValue.Denominator).ToCanonical();
/// <summary>
/// Adds two objects of <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to add.</param>
/// <param name="right">The second object to add.</param>
/// <returns>An object that is the sum of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator +(RationalValue left, RationalValue right)
{
if (left.Denominator == right.Denominator)
return new RationalValue(left.Numerator + right.Numerator, left.Denominator).ToCanonical();
var numerator = left.Numerator * right.Denominator + right.Numerator * left.Denominator;
var denominator = left.Denominator * right.Denominator;
return new RationalValue(numerator, denominator).ToCanonical();
}
/// <summary>
/// Adds <see cref="double"/> and <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to add.</param>
/// <param name="right">The second object to add.</param>
/// <returns>An object that is the sum of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator +(NumberValue left, RationalValue right)
=> new RationalValue(left, NumberValue.One) + right;
/// <summary>
/// Adds <see cref="RationalValue"/> and <see cref="double"/>.
/// </summary>
/// <param name="left">The first object to add.</param>
/// <param name="right">The second object to add.</param>
/// <returns>An object that is the sum of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator +(RationalValue left, NumberValue right)
=> left + new RationalValue(right, NumberValue.One);
/// <summary>
/// Subtracts two objects of <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to sub.</param>
/// <param name="right">The second object to sub.</param>
/// <returns>An object that is the difference of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator -(RationalValue left, RationalValue right)
{
if (left.Denominator == right.Denominator)
return new RationalValue(left.Numerator - right.Numerator, left.Denominator).ToCanonical();
var numerator = left.Numerator * right.Denominator - right.Numerator * left.Denominator;
var denominator = left.Denominator * right.Denominator;
return new RationalValue(numerator, denominator).ToCanonical();
}
/// <summary>
/// Subtracts <see cref="double"/> and <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to sub.</param>
/// <param name="right">The second object to sub.</param>
/// <returns>An object that is the difference of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator -(NumberValue left, RationalValue right)
=> new RationalValue(left, NumberValue.One) - right;
/// <summary>
/// Subtracts <see cref="RationalValue"/> and <see cref="double"/>.
/// </summary>
/// <param name="left">The first object to sub.</param>
/// <param name="right">The second object to sub.</param>
/// <returns>An object that is the difference of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator -(RationalValue left, NumberValue right)
=> left - new RationalValue(right, NumberValue.One);
/// <summary>
/// Multiplies two objects of <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator *(RationalValue left, RationalValue right)
{
var numerator = left.Numerator * right.Numerator;
var denominator = left.Denominator * right.Denominator;
return new RationalValue(numerator, denominator).ToCanonical();
}
/// <summary>
/// Multiplies <see cref="double"/> and <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator *(NumberValue left, RationalValue right)
=> new RationalValue(left, NumberValue.One) * right;
/// <summary>
/// Multiplies <see cref="RationalValue"/> and <see cref="double"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator *(RationalValue left, NumberValue right)
=> left * new RationalValue(right, NumberValue.One);
/// <summary>
/// Divides two objects of <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to divide.</param>
/// <param name="right">The second object to divide.</param>
/// <returns>An object that is the fraction of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator /(RationalValue left, RationalValue right)
{
var numerator = left.Numerator * right.Denominator;
var denominator = left.Denominator * right.Numerator;
return new RationalValue(numerator, denominator).ToCanonical();
}
/// <summary>
/// Divides <see cref="double"/> and <see cref="RationalValue"/>.
/// </summary>
/// <param name="left">The first object to divide.</param>
/// <param name="right">The second object to divide.</param>
/// <returns>An object that is the fraction of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator /(NumberValue left, RationalValue right)
=> new RationalValue(left, NumberValue.One) / right;
/// <summary>
/// Divides <see cref="RationalValue"/> and <see cref="double"/>.
/// </summary>
/// <param name="left">The first object to divide.</param>
/// <param name="right">The second object to divide.</param>
/// <returns>An object that is the fraction of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static RationalValue operator /(RationalValue left, NumberValue right)
=> left / new RationalValue(right, NumberValue.One);
/// <inheritdoc />
public bool Equals(RationalValue other)
=> Numerator * other.Denominator == Denominator * other.Numerator;
/// <inheritdoc />
public override bool Equals(object? obj)
=> obj is RationalValue other && Equals(other);
/// <inheritdoc />
public int CompareTo(RationalValue other)
{
var left = ToCanonical();
var right = other.ToCanonical();
var result = left.Numerator * right.Denominator - left.Denominator * right.Numerator;
if (result < 0)
return -1;
if (result > 0)
return 1;
return 0;
}
/// <inheritdoc />
public int CompareTo(object? obj)
{
if (ReferenceEquals(null, obj))
return 1;
return obj is RationalValue other
? CompareTo(other)
: throw new ArgumentException($"Object must be of type {nameof(RationalValue)}");
}
/// <inheritdoc />
[ExcludeFromCodeCoverage]
public override int GetHashCode()
=> HashCode.Combine(Numerator, Denominator);
/// <inheritdoc />
public override string ToString()
=> $"{Numerator} // {Denominator}";
/// <summary>
/// Converts the current rational number to the canonical form.
/// </summary>
/// <returns>The rational number in the canonical form.</returns>
public RationalValue ToCanonical()
{
var gcd = NumberValue.GCD(Numerator, Denominator);
if (gcd == NumberValue.One)
return this;
var numerator = Numerator / gcd;
var denominator = Denominator / gcd;
if (denominator < 0)
{
numerator = -numerator;
denominator = -denominator;
}
return new RationalValue(numerator, denominator);
}
/// <summary>
/// Converts the current rational number to irrational.
/// </summary>
/// <returns>The irrational number.</returns>
public NumberValue ToIrrational()
=> Numerator / Denominator;
/// <summary>
/// Returns the absolute value of a rational number.
/// </summary>
/// <param name="rationalValue">The rational number.</param>
/// <returns>The abs of rational number.</returns>
public static RationalValue Abs(RationalValue rationalValue)
=> new RationalValue(NumberValue.Abs(rationalValue.Numerator), NumberValue.Abs(rationalValue.Denominator));
/// <summary>
/// Returns the rational number raised to the specified power.
/// </summary>
/// <param name="rationalValue">The rational number.</param>
/// <param name="numberValue">The power.</param>
/// <returns>The <paramref name="rationalValue"/> raised to the <paramref name="numberValue"/>.</returns>
public static RationalValue Pow(RationalValue rationalValue, NumberValue numberValue)
{
double numerator;
double denominator;
var power = numberValue.Number;
if (power >= 0)
{
numerator = rationalValue.Numerator.Number;
denominator = rationalValue.Denominator.Number;
}
else
{
numerator = rationalValue.Denominator.Number;
denominator = rationalValue.Numerator.Number;
power = -power;
}
var numeratorPow = Math.Pow(numerator, power);
var denominatorPow = Math.Pow(denominator, power);
return new RationalValue(numeratorPow, denominatorPow).ToCanonical();
}
/// <summary>
/// Returns the logarithm of a specified number in a specified base.
/// </summary>
/// <param name="rationalValue">The rational number whose logarithm is to be found.</param>
/// <param name="base">The base of the logarithm.</param>
/// <returns>The logarithm.</returns>
public static NumberValue Log(RationalValue rationalValue, NumberValue @base)
=> NumberValue.Log(rationalValue.Numerator, @base) - NumberValue.Log(rationalValue.Denominator, @base);
/// <summary>
/// Returns the base 2 logarithm of a specified rational number.
/// </summary>
/// <param name="rationalValue">A rational number whose logarithm is to be found.</param>
/// <returns>The binary logarithm.</returns>
public static NumberValue Lb(RationalValue rationalValue)
=> Log(rationalValue, NumberValue.Two);
/// <summary>
/// Returns the base 10 logarithm of a specified rational number.
/// </summary>
/// <param name="rationalValue">A rational number whose logarithm is to be found.</param>
/// <returns>The base 10 logarithm.</returns>
public static NumberValue Lg(RationalValue rationalValue)
=> Log(rationalValue, new NumberValue(10));
/// <summary>
/// Returns the natural (base <c>e</c>) logarithm of a specified rational number.
/// </summary>
/// <param name="rationalValue">The rational number whose logarithm is to be found.</param>
/// <returns>The natural (base <c>e</c>) logarithm.</returns>
public static NumberValue Ln(RationalValue rationalValue)
=> Log(rationalValue, new NumberValue(Math.E));
/// <summary>
/// Gets the numerator.
/// </summary>
public NumberValue Numerator { get; }
/// <summary>
/// Gets the denominator.
/// </summary>
public NumberValue Denominator { get; }
}