From 64cd6843d805214a0f346e0a27abc40f9fc19616 Mon Sep 17 00:00:00 2001 From: "Pawel Troka, PhD Eng" Date: Fri, 30 Oct 2026 01:24:09 +0100 Subject: [PATCH 1/2] Expose qualified Euclid integer utilities in TSL and reference --- .../ByRefCompletionTests.cs | 52 +++ .../EuclidEvaluationTests.cs | 122 +++++++ .../Autocompletion/AutocompleteProvider.cs | 3 +- .../AutocompleteReflectionSource.cs | 15 +- .../Compilation/TSLCompiler.cs | 3 +- Computator.NET.Core/Static/functions.xml | 326 +++++++++++++++++- Computator.NET.Engine.Tests/EuclidTests.cs | 100 ++++++ Computator.NET.slnx | 2 + build/reference/enrichment.py | 3 + build/reference/euclid.py | 118 +++++++ build/reference/generate.py | 4 +- build/reference/test_euclid.py | 63 ++++ docs/FUNCTION-REFERENCE.md | 52 ++- docs/followups/PORTABLE-ENGINE.md | 9 +- 14 files changed, 855 insertions(+), 17 deletions(-) create mode 100644 Computator.NET.Core.Tests/AutocompletionTests/ByRefCompletionTests.cs create mode 100644 Computator.NET.Core.Tests/EuclidEvaluationTests.cs create mode 100644 Computator.NET.Engine.Tests/EuclidTests.cs create mode 100644 build/reference/euclid.py create mode 100644 build/reference/test_euclid.py diff --git a/Computator.NET.Core.Tests/AutocompletionTests/ByRefCompletionTests.cs b/Computator.NET.Core.Tests/AutocompletionTests/ByRefCompletionTests.cs new file mode 100644 index 00000000..1f5fdb5e --- /dev/null +++ b/Computator.NET.Core.Tests/AutocompletionTests/ByRefCompletionTests.cs @@ -0,0 +1,52 @@ +using System.Linq; +using System.Runtime.InteropServices; +using Computator.NET.Core.Autocompletion.DataSource; +using Computator.NET.Core.Properties; +using NUnit.Framework; + +namespace Computator.NET.Core.Tests.AutocompletionTests +{ + [TestFixture, NonParallelizable] + public class ByRefCompletionTests + { + private static class Signatures + { + public static void Produce(out long value) { value = 1; } + public static void Change(ref long value) { value++; } + public static void Observe(in long value) { } + public static void OutputBuffer([Out] byte[] value) { } + } + + [TestCase("Produce", "out")] + [TestCase("Change", "ref")] + [TestCase("Observe", "in")] + public void ArgumentModifiersAreRetainedInInsertionAndTypedCaption(string method, string modifier) + { + var settings = Settings.Default; + var oldReturn = settings.ShowReturnTypeInScripting; + var oldParameters = settings.ShowParametersTypeInScripting; + try + { + settings.ShowReturnTypeInScripting = settings.ShowParametersTypeInScripting = true; + var item = new AutocompleteReflectionSource().GetFunctionsNamesWithDescription(typeof(Signatures)) + .Single(candidate => candidate.Text.StartsWith(method + "(")); + item.IsScripting = true; + NUnit.Framework.Assert.That(item.GetTextForReplace(), Is.EqualTo(method + "(" + modifier + " value)")); + NUnit.Framework.Assert.That(item.MenuText, Is.EqualTo("void " + method + "(" + modifier + " integer value)")); + } + finally + { + settings.ShowReturnTypeInScripting = oldReturn; + settings.ShowParametersTypeInScripting = oldParameters; + } + } + + [Test] + public void InteropOutputAttributeOnAValueParameterDoesNotInsertAnOutKeyword() + { + var item = new AutocompleteReflectionSource().GetFunctionsNamesWithDescription(typeof(Signatures)) + .Single(candidate => candidate.Text.StartsWith("OutputBuffer(")); + NUnit.Framework.Assert.That(item.GetTextForReplace(), Is.EqualTo("OutputBuffer(value)")); + } + } +} diff --git a/Computator.NET.Core.Tests/EuclidEvaluationTests.cs b/Computator.NET.Core.Tests/EuclidEvaluationTests.cs new file mode 100644 index 00000000..79566f85 --- /dev/null +++ b/Computator.NET.Core.Tests/EuclidEvaluationTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Computator.NET.Core.Autocompletion; +using Computator.NET.Core.Autocompletion.DataSource; +using Computator.NET.Core.Evaluation; +using Computator.NET.Core.Properties; +using Computator.NET.DataTypes; +using NUnit.Framework; + +namespace Computator.NET.Core.Tests +{ + [TestFixture(false)] + [TestFixture(true)] + [NonParallelizable] + public class EuclidEvaluationTests + { + private readonly bool _legacy; + public EuclidEvaluationTests(bool legacy) { _legacy = legacy; } + + private sealed class LegacyEvaluator : ExpressionsEvaluator + { protected override bool UsePrecompiledBuiltins => false; } + private sealed class LegacyScriptEvaluator : ScriptEvaluator + { protected override bool UsePrecompiledBuiltins => false; } + + [TestCase("Euclid.GreatestCommonDivisor(-84,30)", 6)] + [TestCase("Euclid.LeastCommonMultiple(12,18)", 36)] + [TestCase("Euclid.Modulus(-7,3)", 2)] + [TestCase("Euclid.Remainder(-7,3)", -1)] + [TestCase("Euclid.PowerOfTwo((integer)40)", 1099511627776)] + [TestCase("Euclid.IsPerfectSquare(144) ? 1 : 0", 1)] + [TestCase("((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero))", 1.2089258196146292E+24)] + public void DesktopExpressionsUseTheSameQualifiedLibrary(string source, double expected) + { + ExpressionsEvaluator evaluator = _legacy ? new LegacyEvaluator() : new ExpressionsEvaluator(); + Func function = evaluator.Evaluate(source, "", CalculationsMode.Real); + NUnit.Framework.Assert.That(function(0), Is.EqualTo(expected)); + } + + [Test] + public void DesktopScriptRetainsExactIntegersAndOutArguments() + { + const string source = """ + integer u, v; + integer divisor = Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); + write(divisor); write(":["); write(240*u+46*v); write("]:"); + write(Euclid.GreatestCommonDivisor(9007199254740993,0)); + """; + ScriptEvaluator evaluator = _legacy ? new LegacyScriptEvaluator() : new ScriptEvaluator(); + var output = new List(); + evaluator.Evaluate(source, "").Evaluate(output.Add); + NUnit.Framework.Assert.That(string.Concat(output), Is.EqualTo("2:[2]:9007199254740993")); + } + + [Test] + public void ExistingUnqualifiedGcdAliasesKeepTheirLegacySurface() + { + ExpressionsEvaluator evaluator = _legacy ? new LegacyEvaluator() : new ExpressionsEvaluator(); + Func function = evaluator.Evaluate("gcd(12,18)+nwd(12,18)+lcm(4,6)+nww(4,6)", "", CalculationsMode.Real); + NUnit.Framework.Assert.That(function(0), Is.EqualTo(36)); + } + } + + [TestFixture, NonParallelizable] + public class EuclidCompletionTests + { + [TestCase(false)] + [TestCase(true)] + public void ExtendedGcdCompletionInsertsExecutableOutputArguments(bool arbitraryPrecision) + { + var settings = Settings.Default; + var oldReturn = settings.ShowReturnTypeInScripting; + var oldParameters = settings.ShowParametersTypeInScripting; + try + { + settings.ShowReturnTypeInScripting = settings.ShowParametersTypeInScripting = true; + var items = new AutocompleteReflectionSource().GetFunctionsNamesWithDescription( + typeof(MathNet.Numerics.Euclid), fullName: true); + items.ForEach(item => item.IsScripting = true); + var type = arbitraryPrecision ? "BigInteger" : "integer"; + var completion = items.Single(item => item.Text.StartsWith("Euclid.ExtendedGreatestCommonDivisor(") + && item.MenuText.StartsWith(type + " ")); + + NUnit.Framework.Assert.That(completion.GetTextForReplace(), + Is.EqualTo("Euclid.ExtendedGreatestCommonDivisor(a,b,out x,out y)")); + NUnit.Framework.Assert.That(completion.MenuText, + Is.EqualTo(type + " Euclid.ExtendedGreatestCommonDivisor(" + type + " a, " + type + + " b, out " + type + " x, out " + type + " y)")); + + var declarationType = arbitraryPrecision ? "System.Numerics.BigInteger" : "integer"; + var script = declarationType + " a=240,b=46,x,y; var d=" + completion.GetTextForReplace() + + "; write(d); write(\":\"); write(a*x+b*y);"; + var output = new List(); + new ScriptEvaluator().Evaluate(script, "").Evaluate(output.Add); + NUnit.Framework.Assert.That(string.Concat(output), Is.EqualTo("2:2")); + } + finally + { + settings.ShowReturnTypeInScripting = oldReturn; + settings.ShowParametersTypeInScripting = oldParameters; + } + } + + [Test] + public void EveryPinnedEuclidOverloadHasQualifiedCompletionAndAuthoredHelp() + { + var reference = new FunctionsDetailsFileSource(); + var provider = new AutocompleteProvider(new AutocompleteReflectionSource(), reference); + var items = provider.ScriptingAutocompleteItems.Where(item => item.Details.Type == "Euclid").ToArray(); + NUnit.Framework.Assert.That(items.Length, Is.EqualTo(37)); + NUnit.Framework.Assert.That(items.Select(item => item.Text.Split('(')[0]).Distinct().Count(), Is.EqualTo(12)); + foreach (var item in items) + { + NUnit.Framework.Assert.That(item.Text, Does.StartWith("Euclid.")); + NUnit.Framework.Assert.That(item.Details, Is.SameAs(reference.Details[item.Text]), item.Text); + NUnit.Framework.Assert.That(item.Details.Category, Is.EqualTo("Integer arithmetic"), item.Text); + } + NUnit.Framework.Assert.That(provider.ExpressionAutocompleteItems.Select(item => item.Text), + Does.Contain("Euclid.GreatestCommonDivisor(a,b)")); + } + } +} diff --git a/Computator.NET.Core/Autocompletion/AutocompleteProvider.cs b/Computator.NET.Core/Autocompletion/AutocompleteProvider.cs index d41c6b75..54e0da33 100644 --- a/Computator.NET.Core/Autocompletion/AutocompleteProvider.cs +++ b/Computator.NET.Core/Autocompletion/AutocompleteProvider.cs @@ -47,6 +47,7 @@ private AutocompleteItem[] GetAutocompleteItemsForExpressions(bool removeAdvance { var items = _autocompleteReflectionSource.GetFunctionsNamesWithDescription(typeof(ElementaryFunctions)); + items.AddRange(_autocompleteReflectionSource.GetFunctionsNamesWithDescription(typeof(MathNet.Numerics.Euclid), false, true)); items.AddRange(_autocompleteReflectionSource.GetFunctionsNamesWithDescription(typeof(FunctionRoot), false, true)); items.AddRange(_autocompleteReflectionSource.GetFunctionsNamesWithDescription(typeof(Integral), false, true)); items.AddRange(_autocompleteReflectionSource.GetFunctionsNamesWithDescription(typeof(Derivative), false, true)); @@ -164,4 +165,4 @@ private AutocompleteItem[] GetAutocompleteItemsForScripting() public IEnumerable ExpressionAutocompleteItems { get; } public IEnumerable ScriptingAutocompleteItems { get; } } -} \ No newline at end of file +} diff --git a/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs b/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs index b5bb25b0..436fc0c4 100644 --- a/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs +++ b/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs @@ -131,9 +131,18 @@ private static string MakeAddition(MethodInfo m, bool withType) addition += " ..."; } else + { + var parameter = parameters[i]; + var parameterType = parameter.ParameterType; + var modifier = parameterType.IsByRef + ? (parameter.IsOut ? "out " : parameter.IsIn ? "in " : "ref ") : ""; + if (parameterType.IsByRef) + parameterType = parameterType.GetElementType(); + addition += withType - ? TypeNameToAlias(parameters[i].ParameterType.Name) + " " + parameters[i].Name + ", " - : parameters[i].Name + ","; + ? modifier + TypeNameToAlias(parameterType.Name) + " " + parameter.Name + ", " + : modifier + parameter.Name + ","; + } } @@ -264,4 +273,4 @@ private static int GetImageIndexFromType(string typeName) return imageIndex; } } -} \ No newline at end of file +} diff --git a/Computator.NET.Core/Compilation/TSLCompiler.cs b/Computator.NET.Core/Compilation/TSLCompiler.cs index 5069aeb4..cdad6bc2 100644 --- a/Computator.NET.Core/Compilation/TSLCompiler.cs +++ b/Computator.NET.Core/Compilation/TSLCompiler.cs @@ -27,7 +27,8 @@ public class TslCompiler : ITslCompiler using real = System.Double; using complex = System.Numerics.Complex; using natural = System.UInt64; - using integer = System.Int64;"; + using integer = System.Int64; + using Euclid = MathNet.Numerics.Euclid;"; public static readonly string[] Keywords = { diff --git a/Computator.NET.Core/Static/functions.xml b/Computator.NET.Core/Static/functions.xml index 797266e9..fb39be8b 100644 --- a/Computator.NET.Core/Static/functions.xml +++ b/Computator.NET.Core/Static/functions.xml @@ -5714,6 +5714,316 @@ ]]> https://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Erlang.htm + + Round up to a power of two + Euclid + Integer arithmetic + +

integer Euclid.CeilingToPowerOfTwo(integer number)

+

Finds the smallest representable power of two at least as large as a positive integer.

+

Definition / convention: For number>0, result=2^ceil(log2(number)).

+

Arguments / value: Positive int up to 2^30 or positive integer (64-bit) up to 2^62.

+

Important: An exact power of two is unchanged. Larger positive inputs throw ArgumentOutOfRangeException. Math.NET 5.0 returns 0 for zero and negative inputs, so validate positivity before treating the result as a power of two.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.CeilingToPowerOfTwo(9) = 16

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Extended Euclidean algorithm + Euclid + Integer arithmetic + +

BigInteger Euclid.ExtendedGreatestCommonDivisor(BigInteger a, BigInteger b, out BigInteger x, out BigInteger y)
integer Euclid.ExtendedGreatestCommonDivisor(integer a, integer b, out integer x, out integer y)

+

Extended Euclidean algorithm

+

Returns a greatest common divisor and writes two Bezout coefficients to the out arguments.

+

Definition / convention: a*x+b*y=gcd(a,b).

+

Arguments / value: Arbitrary-precision BigInteger values. The final x,y arguments are output variables, not evaluation coordinates.

+

Important: Declare both coefficient variables first and pass them with out: integer u,v; integer d=Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); then 240*u+46*v equals d=2. Use System.Numerics.BigInteger variables for the BigInteger overload. The coefficient pair is not unique; check its identity, not a particular pair of coefficients.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Extended Euclidean algorithm

+

Returns a greatest common divisor and writes two Bezout coefficients to the out arguments.

+

Definition / convention: a*x+b*y=gcd(a,b).

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values. The final x,y arguments are output variables, not evaluation coordinates.

+

Important: Declare both coefficient variables first and pass them with out: integer u,v; integer d=Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); then 240*u+46*v equals d=2. Use System.Numerics.BigInteger variables for the BigInteger overload. The coefficient pair is not unique; check its identity, not a particular pair of coefficients. Extreme signed 64-bit intermediates can overflow or wrap; choose BigInteger for exact large calculations.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Greatest common divisor of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.GreatestCommonDivisor(BigInteger a, BigInteger b)
integer Euclid.GreatestCommonDivisor(integer a, integer b)

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: The two-value operation accepts negative numbers and zeros: gcd(a,0)=|a| and gcd(0,0)=0. The returned value is an integer, so scripts can retain values above the exact range of real numbers.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: The two-value operation accepts negative numbers and zeros: gcd(a,0)=|a| and gcd(0,0)=0. The returned value is an integer, so scripts can retain values above the exact range of real numbers. Extreme 64-bit inputs can throw OverflowException, including an unrepresentable positive gcd of 2^63. Use BigInteger for the full integer domain.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Greatest common divisor of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.GreatestCommonDivisor(IList`1 integers)
integer Euclid.GreatestCommonDivisor(IList`1 integers)

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: The empty collection returns 0; null throws ArgumentNullException. Math.NET 5.0 starts its reduction at the first value and stops while its accumulated gcd is 0 or 1. A leading zero therefore incorrectly returns 0 even when later values are nonzero. Remove leading zeros or explicitly fold the two-value overload, starting from 0.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: The empty collection returns 0; null throws ArgumentNullException. Math.NET 5.0 starts its reduction at the first value and stops while its accumulated gcd is 0 or 1. A leading zero therefore incorrectly returns 0 even when later values are nonzero. Remove leading zeros or explicitly fold the two-value overload, starting from 0. Extreme 64-bit inputs can throw OverflowException, including an unrepresentable positive gcd of 2^63. Use BigInteger for the full integer domain.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Greatest common divisor of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.GreatestCommonDivisor(BigInteger integers1, BigInteger integers2, ...)
integer Euclid.GreatestCommonDivisor(integer integers1, integer integers2, ...)

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: The empty collection returns 0; null throws ArgumentNullException. Math.NET 5.0 starts its reduction at the first value and stops while its accumulated gcd is 0 or 1. A leading zero therefore incorrectly returns 0 even when later values are nonzero. Remove leading zeros or explicitly fold the two-value overload, starting from 0.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Greatest common divisor of integers

+

Finds the nonnegative common factor that divides every supplied integer.

+

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: The empty collection returns 0; null throws ArgumentNullException. Math.NET 5.0 starts its reduction at the first value and stops while its accumulated gcd is 0 or 1. A leading zero therefore incorrectly returns 0 even when later values are nonzero. Remove leading zeros or explicitly fold the two-value overload, starting from 0. Extreme 64-bit inputs can throw OverflowException, including an unrepresentable positive gcd of 2^63. Use BigInteger for the full integer domain.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.GreatestCommonDivisor(-84,30) = 6

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Even-integer predicate + Euclid + Integer arithmetic + +

bool Euclid.IsEven(integer number)

+

Tests whether an integer is divisible by two.

+

Definition / convention: number=2*k for some integer k.

+

Arguments / value: Signed 32-bit or 64-bit integer.

+

Important: Zero and negative even numbers return true. The result is bool, suitable for if statements or conditional expressions.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.IsEven(-2) = true

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Odd-integer predicate + Euclid + Integer arithmetic + +

bool Euclid.IsOdd(integer number)

+

Tests whether an integer has an odd remainder on division by two.

+

Definition / convention: number=2*k+1 for some integer k.

+

Arguments / value: Signed 32-bit or 64-bit integer.

+

Important: Negative odd numbers return true; zero returns false. The result is bool.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.IsOdd(-3) = true

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Perfect-square predicate + Euclid + Integer arithmetic + +

bool Euclid.IsPerfectSquare(integer number)

+

Tests whether a nonnegative integer is the square of an integer.

+

Definition / convention: number=k*k for some integer k.

+

Arguments / value: Signed 32-bit or 64-bit integer.

+

Important: Zero is a perfect square; negative inputs return false. This is an integer test, not an approximate test for real values near a square.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.IsPerfectSquare(144) = true

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Positive power-of-two predicate + Euclid + Integer arithmetic + +

bool Euclid.IsPowerOfTwo(integer number)

+

Tests whether a positive integer contains exactly one set binary bit.

+

Definition / convention: number=2^k for an integer k>=0.

+

Arguments / value: Signed 32-bit or 64-bit integer.

+

Important: One returns true. Zero and every negative value return false. The bool result is useful when selecting binary grid or transform sizes.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.IsPowerOfTwo(1024) = true

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Least common multiple of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.LeastCommonMultiple(BigInteger a, BigInteger b)
integer Euclid.LeastCommonMultiple(integer a, integer b)

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods. The 64-bit implementation divides before multiplying, but a large product can still silently wrap or throw. Use BigInteger when the result or intermediate arithmetic may exceed signed 64-bit range.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Least common multiple of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.LeastCommonMultiple(IList`1 integers)
integer Euclid.LeastCommonMultiple(IList`1 integers)

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods. The empty collection returns 1 and null throws ArgumentNullException. Collection overloads reduce from the first value.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods. The empty collection returns 1 and null throws ArgumentNullException. Collection overloads reduce from the first value. The 64-bit implementation divides before multiplying, but a large product can still silently wrap or throw. Use BigInteger when the result or intermediate arithmetic may exceed signed 64-bit range.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Least common multiple of integers + Euclid + Integer arithmetic + +

BigInteger Euclid.LeastCommonMultiple(BigInteger integers1, BigInteger integers2, ...)
integer Euclid.LeastCommonMultiple(integer integers1, integer integers2, ...)

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Arbitrary-precision BigInteger values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods. The empty collection returns 1 and null throws ArgumentNullException. Collection overloads reduce from the first value.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Least common multiple of integers

+

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

+

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

+

Arguments / value: Signed integers; the pair overload uses 64-bit integer values.

+

Important: A zero operand gives 0; signs are removed from the result. For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods. The empty collection returns 1 and null throws ArgumentNullException. Collection overloads reduce from the first value. The 64-bit implementation divides before multiplying, but a large product can still silently wrap or throw. Use BigInteger when the result or intermediate arithmetic may exceed signed 64-bit range.

+

Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). Use a script instead when the exact integer value is required. The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.

+

Example: Euclid.LeastCommonMultiple(12,18) = 36

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Integer binary logarithm + Euclid + Integer arithmetic + +

integer Euclid.Log2(integer number)

+

Returns the position of the highest set bit of a positive 32-bit integer.

+

Definition / convention: For number>0, result=floor(log2(number)).

+

Arguments / value: A positive int (32-bit integer); this method has no 64-bit overload.

+

Important: This integer operation rounds down. Math.NET 5.0 returns 0 for zero and 31 for negative inputs; those implementation values are not mathematical logarithms. Validate number>0 when that meaning is required.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.Log2(1000) = 9

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Canonical modulus + Euclid + Integer arithmetic + +

BigInteger Euclid.Modulus(BigInteger dividend, BigInteger divisor)
Single Euclid.Modulus(Single dividend, Single divisor)
integer Euclid.Modulus(integer dividend, integer divisor)
real Euclid.Modulus(real dividend, real divisor)

+

Returns the residue using the divisor-sign convention.

+

Definition / convention: Modulus(-7,3)=2; Modulus(7,-3)=-2.

+

Arguments / value: dividend and a nonzero divisor of matching supported numeric types; overloads accept int, integer, real, Single and BigInteger.

+

Important: For exact arithmetic and a nonzero divisor, modulus has the divisor sign; remainder has the dividend sign. For example, with dividend -7 and divisor 3 the modulus is 2 while the remainder is -1. Both are zero when the dividend is a multiple of the divisor. A zero integer divisor throws DivideByZeroException; a zero floating divisor produces NaN. Math.NET 5.0 computes ((dividend%divisor)+divisor)%divisor. The added divisor can overflow fixed-width integer or floating arithmetic, and floating addition can round away a small remainder. Use BigInteger for exact integer modulus at large magnitudes.

+

Use canonical modulus when wrapping a signed position into a periodic range, with positive divisor for nonnegative results. Use remainder when matching the language division convention; the two operations differ for operands with opposite signs.

+

Example: Euclid.Modulus(-7,3) = 2

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Exact integer power of two + Euclid + Integer arithmetic + +

integer Euclid.PowerOfTwo(integer exponent)

+

Returns a representable positive integer power of two.

+

Definition / convention: result=2^exponent.

+

Arguments / value: The int overload accepts exponents 0..30; the integer (64-bit) overload accepts 0..62.

+

Important: Out-of-range exponents throw ArgumentOutOfRangeException. An ordinary small integer literal selects int; use (integer)40 to select the 64-bit overload. The result stays an integer in scripts.

+

Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.

+

Example: Euclid.PowerOfTwo((integer)40) = 1099511627776

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
+ + Truncating division remainder + Euclid + Integer arithmetic + +

BigInteger Euclid.Remainder(BigInteger dividend, BigInteger divisor)
Single Euclid.Remainder(Single dividend, Single divisor)
integer Euclid.Remainder(integer dividend, integer divisor)
real Euclid.Remainder(real dividend, real divisor)

+

Returns the same remainder as the % operator.

+

Definition / convention: Remainder(-7,3)=-1; Remainder(7,-3)=1.

+

Arguments / value: dividend and a nonzero divisor of matching supported numeric types; overloads accept int, integer, real, Single and BigInteger.

+

Important: For exact arithmetic and a nonzero divisor, modulus has the divisor sign; remainder has the dividend sign. For example, with dividend -7 and divisor 3 the modulus is 2 while the remainder is -1. Both are zero when the dividend is a multiple of the divisor. A zero integer divisor throws DivideByZeroException; a zero floating divisor produces NaN. Fixed-width signed minimum divided by -1 can overflow. Floating inputs follow the runtime % operator, including its rounding and nonfinite behavior.

+

Use canonical modulus when wrapping a signed position into a periodic range, with positive divisor for nonnegative results. Use remainder when matching the language division convention; the two operations differ for operands with opposite signs.

+

Example: Euclid.Remainder(-7,3) = -1

+

Reference: github.com

+]]>
+ https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs +
Euler–Mascheroni constant MathematicalConstants @@ -19283,12 +19593,12 @@ ]]> https://github.com/PawelTroka/Computator.NET/blob/master/Computator.NET.Core/Functions/ScriptingFunctions.cs - + Read remaining text from a file ScriptingFunctions Script input -

void read<T>(File file, T& output)

+

void read<T>(File file, out T output)

Reads all remaining text from the current file position.

Arguments / value: file is a Computator File object, not a pathname or StreamReader. output is an out argument assigned by the method.

Important: Typed overloads use convert<T>; prefer string, real or complex and handle invalid/empty input. readln can return null at end of file; read returns an empty string when no text remains.

@@ -19297,12 +19607,12 @@ ]]>
https://github.com/PawelTroka/Computator.NET/blob/master/Computator.NET.Core/Functions/ScriptingFunctions.cs
- + Read remaining text from a desktop dialog ScriptingFunctions Script input -

void read<T>(T& output)

+

void read<T>(out T output)

Displays a modal desktop input dialog and converts its result.

Arguments / value: Optional caption labels the dialog. This requires an interactive Windows UI and is not browser/CLI standard input. output is an out argument assigned by the method.

Important: Typed overloads use convert<T>; prefer string, real or complex and handle invalid/empty input. Cancellation may produce empty text and conversion errors; no value should be assumed without handling that outcome.

@@ -19311,12 +19621,12 @@ ]]>
https://github.com/PawelTroka/Computator.NET/blob/master/Computator.NET.Core/Functions/ScriptingFunctions.cs
- + Read remaining text from a desktop dialog ScriptingFunctions Script input -

void read<T>(T& output, string caption)

+

void read<T>(out T output, string caption)

Displays a modal desktop input dialog and converts its result.

Arguments / value: Optional caption labels the dialog. This requires an interactive Windows UI and is not browser/CLI standard input. output is an out argument assigned by the method.

Important: Typed overloads use convert<T>; prefer string, real or complex and handle invalid/empty input. Cancellation may produce empty text and conversion errors; no value should be assumed without handling that outcome.

@@ -19353,12 +19663,12 @@ ]]>
https://github.com/PawelTroka/Computator.NET/blob/master/Computator.NET.Core/Functions/ScriptingFunctions.cs
- + Read one line from a file ScriptingFunctions Script input -

void readln<T>(File file, T& output)

+

void readln<T>(File file, out T output)

Reads up to the next line ending, advancing the wrapper’s reader.

Arguments / value: file is a Computator File object, not a pathname or StreamReader. output is an out argument assigned by the method.

Important: Typed overloads use convert<T>; prefer string, real or complex and handle invalid/empty input. readln can return null at end of file; read returns an empty string when no text remains.

diff --git a/Computator.NET.Engine.Tests/EuclidTests.cs b/Computator.NET.Engine.Tests/EuclidTests.cs new file mode 100644 index 00000000..42fa873d --- /dev/null +++ b/Computator.NET.Engine.Tests/EuclidTests.cs @@ -0,0 +1,100 @@ +using NUnit.Framework; + +namespace Computator.NET.Engine.Tests; + +[TestFixture] +public class EuclidTests +{ + [TestCase("Euclid.GreatestCommonDivisor(-84,30)", 6)] + [TestCase("Euclid.GreatestCommonDivisor(42,0)", 42)] + [TestCase("Euclid.GreatestCommonDivisor(0,0)", 0)] + [TestCase("Euclid.GreatestCommonDivisor(new integer[] {84,30,18})", 6)] + [TestCase("Euclid.LeastCommonMultiple(-12,18)", 36)] + [TestCase("Euclid.LeastCommonMultiple(0,18)", 0)] + [TestCase("Euclid.LeastCommonMultiple(6,10,15)", 30)] + [TestCase("Euclid.Modulus(-7,3)", 2)] + [TestCase("Euclid.Modulus(7,-3)", -2)] + [TestCase("Euclid.Modulus(-7.5,3.0)", 1.5)] + [TestCase("Euclid.Remainder(-7,3)", -1)] + [TestCase("Euclid.Remainder(7,-3)", 1)] + [TestCase("Euclid.IsEven(-2) ? 1 : 0", 1)] + [TestCase("Euclid.IsOdd(-3) ? 1 : 0", 1)] + [TestCase("Euclid.IsPowerOfTwo(1024) ? 1 : 0", 1)] + [TestCase("Euclid.IsPowerOfTwo(0) ? 1 : 0", 0)] + [TestCase("Euclid.IsPerfectSquare(144) ? 1 : 0", 1)] + [TestCase("Euclid.IsPerfectSquare(145) ? 1 : 0", 0)] + [TestCase("Euclid.IsPerfectSquare(-1) ? 1 : 0", 0)] + [TestCase("Euclid.PowerOfTwo(10)", 1024)] + [TestCase("Euclid.PowerOfTwo((integer)40)", 1099511627776)] + [TestCase("Euclid.CeilingToPowerOfTwo(9)", 16)] + [TestCase("Euclid.CeilingToPowerOfTwo(0)", 0)] + [TestCase("Euclid.Log2(1000)", 9)] + public void QualifiedHelpersEvaluateInRealExpressions(string expression, double expected) => + NUnit.Framework.Assert.That(new PortableEngine().EvaluateReal(expression), Is.EqualTo(expected)); + + [Test] + public void ScriptResultsKeepIntegersAboveTheExactDoubleRange() + { + var output = new PortableEngine().ExecuteScript( + "integer result = Euclid.GreatestCommonDivisor(9007199254740993,0); write(result);"); + NUnit.Framework.Assert.That(output, Is.EqualTo("9007199254740993")); + } + + [Test] + public void ExtendedGcdSuppliesBezoutCoefficientsThroughOutArguments() + { + const string script = """ + integer u, v; + integer divisor = Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); + write(divisor); write(":"); write(240*u+46*v); + """; + NUnit.Framework.Assert.That(new PortableEngine().ExecuteScript(script), Is.EqualTo("2:2")); + } + + [Test] + public void BigIntegerOverloadsAvoidFixedWidthOverflow() + { + const string script = """ + var scale = System.Numerics.BigInteger.One << 80; + var divisor = Euclid.GreatestCommonDivisor(3*scale,5*scale); + var multiple = Euclid.LeastCommonMultiple(3*scale,5*scale); + System.Numerics.BigInteger u, v; + var extended = Euclid.ExtendedGreatestCommonDivisor(3*scale,5*scale,out u,out v); + write(divisor); write(":"); write(multiple); + write(":"); write(3*scale*u+5*scale*v == extended); + """; + NUnit.Framework.Assert.That(new PortableEngine().ExecuteScript(script), + Is.EqualTo("1208925819614629174706176:18133887294219437620592640:True")); + } + + [Test] + public void BigIntegerCalculatorResultUsesTheDocumentedExplicitConversion() + { + const string expression = "((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero))"; + NUnit.Framework.Assert.That(new PortableEngine().EvaluateReal(expression), + Is.EqualTo(Math.ScaleB(1, 80))); + } + + [Test] + public void AliasIsAvailableInsideCustomFunctionDefinitions() + { + const string definitions = "static integer period(integer a, integer b) { return Euclid.LeastCommonMultiple(a,b); }"; + NUnit.Framework.Assert.That(new PortableEngine().EvaluateReal("period(12,18)", customFunctions: definitions), + Is.EqualTo(36)); + } + + [TestCase("Euclid.PowerOfTwo(-1)")] + [TestCase("Euclid.PowerOfTwo(31)")] + [TestCase("Euclid.PowerOfTwo((integer)63)")] + [TestCase("Euclid.CeilingToPowerOfTwo(1073741825)")] + public void UpstreamPowerBoundsRemainVisible(string expression) => + NUnit.Framework.Assert.Throws(() => new PortableEngine().EvaluateReal(expression)); + + [Test] + public void IntegerZeroDivisorIsAnErrorAndFloatingZeroDivisorIsNaN() + { + var engine = new PortableEngine(); + NUnit.Framework.Assert.Throws(() => engine.EvaluateReal("Euclid.Modulus(7,0)")); + NUnit.Framework.Assert.That(engine.EvaluateReal("Euclid.Modulus(7.0,0.0)"), Is.NaN); + } +} diff --git a/Computator.NET.slnx b/Computator.NET.slnx index 77c6a057..9212d1fe 100644 --- a/Computator.NET.slnx +++ b/Computator.NET.slnx @@ -335,6 +335,7 @@ + @@ -348,6 +349,7 @@ + diff --git a/build/reference/enrichment.py b/build/reference/enrichment.py index 0940369b..54adcc11 100644 --- a/build/reference/enrichment.py +++ b/build/reference/enrichment.py @@ -285,6 +285,9 @@ def value_tex(value): def enrich(item, article, values): kind=item['type']; signature=item['signature']; name=signature.split('(')[0] + if kind == 'Euclid': + from euclid import teaching + return Enrichment(paragraphs=(teaching(name.split('.')[-1]),)) if kind in distributions.FAMILIES: op=name.split('.')[-1]; notes=[MODEL_LESSONS[kind]] if op=='CDF': diff --git a/build/reference/euclid.py b/build/reference/euclid.py new file mode 100644 index 00000000..aceb7712 --- /dev/null +++ b/build/reference/euclid.py @@ -0,0 +1,118 @@ +"""The qualified, pinned Math.NET Euclid surface; no replacement algorithms.""" +from common import A + +SOURCE = 'https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs' + + +def describe(item, values): + signature = item['signature'] + name = signature.split('(')[0].split('.')[-1] + collection = '(integers' in signature + big = 'BigInteger' in item['menu'] + integer = 'Arbitrary-precision BigInteger values.' if big else 'Signed integers; the pair overload uses 64-bit integer values.' + + if name == 'GreatestCommonDivisor': + note = ('The two-value operation accepts negative numbers and zeros: gcd(a,0)=|a| and gcd(0,0)=0. ' + 'The returned value is an integer, so scripts can retain values above the exact range of real numbers.') + if collection: + note = ('The empty collection returns 0; null throws ArgumentNullException. ' + 'Math.NET 5.0 starts its reduction at the first value and stops while its accumulated gcd is 0 or 1. ' + 'A leading zero therefore incorrectly returns 0 even when later values are nonzero. ' + 'Remove leading zeros or explicitly fold the two-value overload, starting from 0.') + if not big: + note += ' Extreme 64-bit inputs can throw OverflowException, including an unrepresentable positive gcd of 2^63. Use BigInteger for the full integer domain.' + return A('Greatest common divisor of integers', + 'Finds the nonnegative common factor that divides every supplied integer.', + 'gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).', integer, + note, 'Euclid.GreatestCommonDivisor(-84,30) = 6', SOURCE, 'Integer arithmetic') + + if name == 'LeastCommonMultiple': + note = ('A zero operand gives 0; signs are removed from the result. ' + 'For nonzero operands this is the smallest positive shared multiple, useful for combining repeating integer periods.') + if collection: + note += ' The empty collection returns 1 and null throws ArgumentNullException. Collection overloads reduce from the first value.' + if not big: + note += (' The 64-bit implementation divides before multiplying, but a large product can still silently wrap or throw. ' + 'Use BigInteger when the result or intermediate arithmetic may exceed signed 64-bit range.') + return A('Least common multiple of integers', + 'Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.', + 'lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.', integer, + note, 'Euclid.LeastCommonMultiple(12,18) = 36', SOURCE, 'Integer arithmetic') + + if name == 'ExtendedGreatestCommonDivisor': + note = ('Declare both coefficient variables first and pass them with out: ' + 'integer u,v; integer d=Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); ' + 'then 240*u+46*v equals d=2. Use System.Numerics.BigInteger variables for the BigInteger overload. ' + 'The coefficient pair is not unique; check its identity, not a particular pair of coefficients.') + if not big: + note += ' Extreme signed 64-bit intermediates can overflow or wrap; choose BigInteger for exact large calculations.' + return A('Extended Euclidean algorithm', + 'Returns a greatest common divisor and writes two Bezout coefficients to the out arguments.', + 'a*x+b*y=gcd(a,b).', integer+' The final x,y arguments are output variables, not evaluation coordinates.', + note, '', SOURCE, 'Integer arithmetic') + + if name in ('Modulus', 'Remainder'): + modulus = name == 'Modulus' + note = ('For exact arithmetic and a nonzero divisor, modulus has the divisor sign; remainder has the dividend sign. ' + 'For example, with dividend -7 and divisor 3 the modulus is 2 while the remainder is -1. ' + 'Both are zero when the dividend is a multiple of the divisor. ' + 'A zero integer divisor throws DivideByZeroException; a zero floating divisor produces NaN.') + if modulus: + note += (' Math.NET 5.0 computes ((dividend%divisor)+divisor)%divisor. ' + 'The added divisor can overflow fixed-width integer or floating arithmetic, and floating addition can round away a small remainder. ' + 'Use BigInteger for exact integer modulus at large magnitudes.') + else: + note += ' Fixed-width signed minimum divided by -1 can overflow. Floating inputs follow the runtime % operator, including its rounding and nonfinite behavior.' + return A('Canonical modulus' if modulus else 'Truncating division remainder', + 'Returns the residue using the divisor-sign convention.' if modulus else 'Returns the same remainder as the % operator.', + 'Modulus(-7,3)=2; Modulus(7,-3)=-2.' if modulus else 'Remainder(-7,3)=-1; Remainder(7,-3)=1.', + 'dividend and a nonzero divisor of matching supported numeric types; overloads accept int, integer, real, Single and BigInteger.', + note, 'Euclid.'+name+'(-7,3) = '+('2' if modulus else '-1'), SOURCE, 'Integer arithmetic') + + data = { + 'IsEven': ('Even-integer predicate', 'Tests whether an integer is divisible by two.', + 'number=2*k for some integer k.', 'Signed 32-bit or 64-bit integer.', + 'Zero and negative even numbers return true. The result is bool, suitable for if statements or conditional expressions.', + 'Euclid.IsEven(-2) = true'), + 'IsOdd': ('Odd-integer predicate', 'Tests whether an integer has an odd remainder on division by two.', + 'number=2*k+1 for some integer k.', 'Signed 32-bit or 64-bit integer.', + 'Negative odd numbers return true; zero returns false. The result is bool.', + 'Euclid.IsOdd(-3) = true'), + 'IsPowerOfTwo': ('Positive power-of-two predicate', 'Tests whether a positive integer contains exactly one set binary bit.', + 'number=2^k for an integer k>=0.', 'Signed 32-bit or 64-bit integer.', + 'One returns true. Zero and every negative value return false. The bool result is useful when selecting binary grid or transform sizes.', + 'Euclid.IsPowerOfTwo(1024) = true'), + 'IsPerfectSquare': ('Perfect-square predicate', 'Tests whether a nonnegative integer is the square of an integer.', + 'number=k*k for some integer k.', 'Signed 32-bit or 64-bit integer.', + 'Zero is a perfect square; negative inputs return false. This is an integer test, not an approximate test for real values near a square.', + 'Euclid.IsPerfectSquare(144) = true'), + 'PowerOfTwo': ('Exact integer power of two', 'Returns a representable positive integer power of two.', + 'result=2^exponent.', 'The int overload accepts exponents 0..30; the integer (64-bit) overload accepts 0..62.', + 'Out-of-range exponents throw ArgumentOutOfRangeException. An ordinary small integer literal selects int; use (integer)40 to select the 64-bit overload. The result stays an integer in scripts.', + 'Euclid.PowerOfTwo((integer)40) = 1099511627776'), + 'Log2': ('Integer binary logarithm', 'Returns the position of the highest set bit of a positive 32-bit integer.', + 'For number>0, result=floor(log2(number)).', 'A positive int (32-bit integer); this method has no 64-bit overload.', + 'This integer operation rounds down. Math.NET 5.0 returns 0 for zero and 31 for negative inputs; those implementation values are not mathematical logarithms. Validate number>0 when that meaning is required.', + 'Euclid.Log2(1000) = 9'), + 'CeilingToPowerOfTwo': ('Round up to a power of two', 'Finds the smallest representable power of two at least as large as a positive integer.', + 'For number>0, result=2^ceil(log2(number)).', 'Positive int up to 2^30 or positive integer (64-bit) up to 2^62.', + 'An exact power of two is unchanged. Larger positive inputs throw ArgumentOutOfRangeException. Math.NET 5.0 returns 0 for zero and negative inputs, so validate positivity before treating the result as a power of two.', + 'Euclid.CeilingToPowerOfTwo(9) = 16') + } + title, definition, formula, arguments, note, example = data[name] + return A(title, definition, formula, arguments, note, example, SOURCE, 'Integer arithmetic') + + +def teaching(name): + if name in ('GreatestCommonDivisor', 'LeastCommonMultiple', 'ExtendedGreatestCommonDivisor'): + return ('Keep exact integer results in integer or System.Numerics.BigInteger variables in scripts. ' + 'Fixed-width integer results convert to real in the calculator; above 2^53 that conversion can round an otherwise exact integer. ' + 'BigInteger results require an explicit conversion, with the whole cast expression parenthesized: ' + '((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)). ' + 'Use a script instead when the exact integer value is required. ' + 'The qualified Euclid methods are separate from the existing gcd, nwd, lcm and nww convenience functions.') + if name in ('Modulus', 'Remainder'): + return ('Use canonical modulus when wrapping a signed position into a periodic range, with positive divisor for nonnegative results. ' + 'Use remainder when matching the language division convention; the two operations differ for operands with opposite signs.') + return ('Use these integer predicates and size calculations before allocating a numerical grid or selecting an algorithm. ' + 'A bool result belongs in a script condition or a conditional expression such as Euclid.IsEven(4) ? 1 : 0.') diff --git a/build/reference/generate.py b/build/reference/generate.py index 781c85d7..03775610 100644 --- a/build/reference/generate.py +++ b/build/reference/generate.py @@ -14,7 +14,7 @@ import re import xml.etree.ElementTree as ET from urllib.parse import urlparse -import elementary, physical, mathematical, special, scientific, distributions +import elementary, physical, mathematical, special, scientific, distributions, euclid from enrichment import enrich from common import InlineMath, Prose from functools import lru_cache @@ -86,7 +86,7 @@ def em(value):return format(value,'.3f').rstrip('0').rstrip('.')+'em' return 'width:'+em(int(image.get('width'))/20)+';height:'+em(int(image.get('height'))/20)+';vertical-align:'+em(-int(image.get('data-depth'))/1000)+';' MAPPINGS={'ElementaryFunctions':elementary,'PhysicalConstants':physical, - 'MathematicalConstants':mathematical,'SpecialFunctions':special} + 'MathematicalConstants':mathematical,'SpecialFunctions':special,'Euclid':euclid} SCIENCE={'MatrixFunctions','ScriptingFunctions','StatisticsFunctions','Integral','Derivative', 'FunctionRoot','MathematicalTransformations'} ALLOWED_TAGS={'div','p','strong','b','code','pre','br','a','ul','li','sup','sub','img'} diff --git a/build/reference/test_euclid.py b/build/reference/test_euclid.py new file mode 100644 index 00000000..20dae5d4 --- /dev/null +++ b/build/reference/test_euclid.py @@ -0,0 +1,63 @@ +"""Keep the qualified integer reference honest about the pinned library.""" +import unittest +from generate import articles + + +class EuclidReferenceTests(unittest.TestCase): + def article(self, signature, menu): + return articles({'items': [{'signature': signature, 'menu': menu, 'type': 'Euclid'}], + 'constants': []})[0] + + def test_gcd_and_lcm_are_distinct_from_the_legacy_short_names(self): + for method in ('GreatestCommonDivisor', 'LeastCommonMultiple'): + with self.subTest(method=method): + result = self.article('Euclid.'+method+'(a,b)', 'integer Euclid.'+method+'(integer a, integer b)') + self.assertEqual(result['category'], 'Integer arithmetic') + self.assertIn('existing gcd, nwd, lcm and nww', result['description']) + self.assertIn('above 2^53', result['description']) + self.assertIn('BigInteger', result['description']) + self.assertIn('BigInteger results require an explicit conversion', result['description']) + self.assertIn('((real)Euclid.GreatestCommonDivisor', result['description']) + + def test_collection_gcd_documents_the_upstream_leading_zero_defect(self): + result = self.article('Euclid.GreatestCommonDivisor(integers)', + 'integer Euclid.GreatestCommonDivisor(IList`1 integers)') + self.assertIn('leading zero therefore incorrectly returns 0', result['description']) + self.assertIn('explicitly fold the two-value overload', result['description']) + + def test_extended_gcd_teaches_out_arguments_and_the_independent_identity(self): + result = self.article('Euclid.ExtendedGreatestCommonDivisor(a,b,out x,out y)', + 'integer Euclid.ExtendedGreatestCommonDivisor(integer a, integer b, out integer x, out integer y)') + self.assertIn('out u,out v', result['description']) + self.assertIn('240*u+46*v equals d=2', result['description']) + self.assertIn('coefficient pair is not unique', result['description']) + + def test_lcm_uses_the_smallest_positive_multiple_for_nonzero_operands(self): + result = self.article('Euclid.LeastCommonMultiple(a,b)', + 'integer Euclid.LeastCommonMultiple(integer a, integer b)') + self.assertIn('smallest positive shared multiple', result['description']) + self.assertNotIn('first nonnegative shared multiple', result['description']) + + def test_modulus_and_remainder_explain_opposite_sign_inputs(self): + for method in ('Modulus', 'Remainder'): + with self.subTest(method=method): + result = self.article('Euclid.'+method+'(dividend,divisor)', + 'integer Euclid.'+method+'(integer dividend, integer divisor)') + self.assertIn('modulus is 2 while the remainder is -1', result['description']) + self.assertIn('zero integer divisor throws DivideByZeroException', result['description']) + if method == 'Modulus': + self.assertIn('can overflow fixed-width', result['description']) + + def test_power_of_two_explains_int32_vs_int64_overload_selection(self): + result = self.article('Euclid.PowerOfTwo(exponent)', 'integer Euclid.PowerOfTwo(integer exponent)') + self.assertIn('0..30', result['description']) + self.assertIn('0..62', result['description']) + self.assertIn('(integer)40', result['description']) + + def test_unreviewed_euclid_method_is_rejected(self): + with self.assertRaises(KeyError): + self.article('Euclid.Unknown(number)', 'integer Euclid.Unknown(integer number)') + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/FUNCTION-REFERENCE.md b/docs/FUNCTION-REFERENCE.md index 6ba097c4..31f91e72 100644 --- a/docs/FUNCTION-REFERENCE.md +++ b/docs/FUNCTION-REFERENCE.md @@ -1,6 +1,6 @@ # Built-in function and constant reference -This reference documents the existing application, not a replacement mathematical API. It covers every non-keyword entry returned by the runtime scripting-completion catalog, including the expression subset. The reviewed baseline has **1,220 unique lookup signatures** across **1,229 non-keyword completion items**: 120 elementary functions, 224 special-function signatures, 88 mathematical values, 342 physical constants, and the exposed distribution, numerical, matrix, random, transform and scripting operations. The other 65 completion items are language keywords, not mathematical functions/constants. +This reference documents the existing application, not a replacement mathematical API. It covers every non-keyword entry returned by the runtime scripting-completion catalog, including the expression subset. The reviewed catalog has **1,236 unique lookup signatures** across **1,266 non-keyword completion items**: 120 elementary functions, 224 special-function signatures, 88 mathematical values, 342 physical constants, 16 qualified Euclid entries, and the exposed distribution, numerical, matrix, random, transform and scripting operations. The other 65 completion items are language keywords, not mathematical functions/constants. ## Reading it @@ -45,6 +45,56 @@ Generation rejects missing subjects, labels that only repeat the runtime type, c Text is newly written and formulas are mathematical definitions; no third-party article or image bundle is copied into the application. Source pages are optional reference reading, not assets downloaded during execution. The editorial review date is **2026-10-16**. A source citation does not turn a rounded legacy number into an exact or continuously updated constant. +## Qualified integer utilities + +`Euclid` exposes the already pinned [Math.NET Numerics 5.0.0 integer utilities](https://github.com/mathnet/mathnet-numerics/blob/v5.0.0/src/Numerics/Euclid.cs) +through the shared TSL preamble in desktop, web API and portable execution. The +desktop/web reference includes all 37 public overloads under 16 lookup entries. +No new numerical implementation or dependency is introduced. + +| Task | Example | Result | +| --- | --- | --- | +| Common divisor, including signed inputs | `Euclid.GreatestCommonDivisor(-84,30)` | `6` | +| Combine repeating periods | `Euclid.LeastCommonMultiple(12,18)` | `36` | +| Wrap a negative position | `Euclid.Modulus(-7,3)` | `2` | +| Preserve the language remainder convention | `Euclid.Remainder(-7,3)` | `-1` | +| Check an integer square | `Euclid.IsPerfectSquare(144)` | `true` | +| Select a binary grid size | `Euclid.CeilingToPowerOfTwo(9)` | `16` | +| Request a 64-bit power of two | `Euclid.PowerOfTwo((integer)40)` | `1099511627776` | + +For Bezout coefficients, use a script and explicit output arguments: + +```csharp +integer u, v; +integer d = Euclid.ExtendedGreatestCommonDivisor(240,46,out u,out v); +write(d); // 2 +write(240*u+46*v); // 2 +``` + +The names `gcd`, `nwd`, `lcm` and `nww` retain their existing behavior. Qualified +`Euclid.*` calls follow the pinned library's overloads and error contracts. Use +`System.Numerics.BigInteger` overloads for exact large GCD/LCM/coefficient +calculations; fixed-width LCM and modulus can overflow internally, sometimes +without an exception. Scripts can retain exact integer values. Fixed-width +integer results convert to `real` in the calculator and can round above 2^53. +`BigInteger` results require an explicit conversion; enclose the whole cast +expression in parentheses so it passes the existing expression parser: + +```csharp +((real)Euclid.GreatestCommonDivisor(System.Numerics.BigInteger.One<<80,System.Numerics.BigInteger.Zero)) +``` + +This returns approximately `1.2089258196146292E+24`; use a script to retain +the exact integer `1208925819614629174706176`. + +The articles explicitly document library edge cases rather than presenting the +alias as a correction to Math.NET: collection GCD mishandles a leading zero, +`Log2` has nonmathematical results for nonpositive inputs, and rounding to a power +of two returns zero for nonpositive inputs. Prefer the pair GCD overload when +folding data that can start with zero. Integer power methods reject results that +cannot fit their selected signed type; a small literal selects `int`, while an +`integer` variable or cast selects the 64-bit overload. + ## Corrected incomplete-beta parameter order `Beta(x,a,b)`, its `β(x,a,b)` alias and `BetaNorm(x,a,b)` now pass the two diff --git a/docs/followups/PORTABLE-ENGINE.md b/docs/followups/PORTABLE-ENGINE.md index 0be6c792..cb2c29f0 100644 --- a/docs/followups/PORTABLE-ENGINE.md +++ b/docs/followups/PORTABLE-ENGINE.md @@ -79,7 +79,14 @@ Use TSL `write(...)` for script result text. Ordinary managed Console writes are ## Supported capabilities and boundaries -The portable surface includes real/two-variable and complex arithmetic, TSL notation, mathematical constants, custom definitions, managed scripts, the imported matrix functions, and the shared integration/differentiation/root services. The scalar adapters (`sin`, `cos`, `tan`, `sqrt`, `exp`, `ln`, `log`, `abs`, `pow`, `floor`, `ceil`, `round`) delegate to .NET's functions. [ScalarFunctions.cs](../../Computator.NET.Engine/ScalarFunctions.cs) defines available scalar spellings; [portable science](PORTABLE-SCIENCE.md) explains shared numerical implementations. +The portable surface includes real/two-variable and complex arithmetic, TSL notation, mathematical constants, custom definitions, managed scripts, the imported matrix functions, qualified `Euclid.*` integer utilities, and the shared integration/differentiation/root services. The scalar adapters (`sin`, `cos`, `tan`, `sqrt`, `exp`, `ln`, `log`, `abs`, `pow`, `floor`, `ceil`, `round`) delegate to .NET's functions. [ScalarFunctions.cs](../../Computator.NET.Engine/ScalarFunctions.cs) defines available scalar spellings; [portable science](PORTABLE-SCIENCE.md) explains shared numerical implementations. + +`Euclid.GreatestCommonDivisor(-84,30)` returns `6`, and +`Euclid.Modulus(-7,3)` returns `2`. These qualified calls use the existing pinned +Math.NET library, including its `System.Numerics.BigInteger` overloads and error +contracts. See [integer utilities and edge cases](../FUNCTION-REFERENCE.md#qualified-integer-utilities) +for script examples, exact-integer output and the distinction between `int` and +64-bit `integer` overloads. The full native GSL/special-function catalog, interactive input dialogs, desktop settings/fonts and chart rendering remain outside this portable surface. An unavailable function produces a compiler diagnostic. The frontend running on macOS/Linux with a Windows API is a separate deployment path. The [reduced Mono desktop](../MONO-DESKTOP.md) is also a separate explicit compatibility target, built from the current shared sources. From 0ea6da7f804c06b502b174d38a005e9bfcb5de45 Mon Sep 17 00:00:00 2001 From: "Pawel Troka, PhD Eng" Date: Fri, 30 Oct 2026 19:19:15 +0100 Subject: [PATCH 2/2] Distinguish Euclid overloads in completion captions and reference --- .../EuclidEvaluationTests.cs | 36 +++++++++++++++++++ .../AutocompleteReflectionSource.cs | 30 ++++++++++++++-- Computator.NET.Core/Static/functions.xml | 22 ++++++------ 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/Computator.NET.Core.Tests/EuclidEvaluationTests.cs b/Computator.NET.Core.Tests/EuclidEvaluationTests.cs index 79566f85..03774575 100644 --- a/Computator.NET.Core.Tests/EuclidEvaluationTests.cs +++ b/Computator.NET.Core.Tests/EuclidEvaluationTests.cs @@ -64,6 +64,42 @@ public void ExistingUnqualifiedGcdAliasesKeepTheirLegacySurface() [TestFixture, NonParallelizable] public class EuclidCompletionTests { + [TestCase(false)] + [TestCase(true)] + public void TypedCaptionsDistinguishEveryEuclidOverload(bool scripting) + { + var settings = Settings.Default; + var oldReturnExpression = settings.ShowReturnTypeInExpression; + var oldParametersExpression = settings.ShowParametersTypeInExpression; + var oldReturnScripting = settings.ShowReturnTypeInScripting; + var oldParametersScripting = settings.ShowParametersTypeInScripting; + try + { + settings.ShowReturnTypeInExpression = settings.ShowReturnTypeInScripting = true; + settings.ShowParametersTypeInExpression = settings.ShowParametersTypeInScripting = true; + var items = new AutocompleteReflectionSource().GetFunctionsNamesWithDescription( + typeof(MathNet.Numerics.Euclid), fullName: true); + items.ForEach(item => item.IsScripting = scripting); + NUnit.Framework.Assert.That(items.Count, Is.EqualTo(37)); + NUnit.Framework.Assert.That(items.Select(item => item.MenuText), Is.Unique); + NUnit.Framework.Assert.That(items.Select(item => item.MenuText), Does.Contain( + "int Euclid.PowerOfTwo(int exponent)")); + NUnit.Framework.Assert.That(items.Select(item => item.MenuText), Does.Contain( + "integer Euclid.PowerOfTwo(integer exponent)")); + NUnit.Framework.Assert.That(items.Select(item => item.MenuText), Does.Contain( + "integer Euclid.GreatestCommonDivisor(IList integers)")); + NUnit.Framework.Assert.That(items.Select(item => item.MenuText), Does.Contain( + "BigInteger Euclid.GreatestCommonDivisor(IList integers)")); + } + finally + { + settings.ShowReturnTypeInExpression = oldReturnExpression; + settings.ShowParametersTypeInExpression = oldParametersExpression; + settings.ShowReturnTypeInScripting = oldReturnScripting; + settings.ShowParametersTypeInScripting = oldParametersScripting; + } + } + [TestCase(false)] [TestCase(true)] public void ExtendedGcdCompletionInsertsExecutableOutputArguments(bool arbitraryPrecision) diff --git a/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs b/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs index 436fc0c4..d6c86529 100644 --- a/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs +++ b/Computator.NET.Core/Autocompletion/DataSource/AutocompleteReflectionSource.cs @@ -52,7 +52,16 @@ public List if (fullName) fullNameExtension = m.ReflectedType.Name + "."; - if (m.IsGenericMethod || m.IsGenericMethodDefinition) + if (m.DeclaringType == typeof(MathNet.Numerics.Euclid)) + { + // Euclid exposes both Int32 and Int64 overloads. Calling + // both "integer" makes distinct operations identical in + // completion, including PowerOfTwo's different bounds. + items.Add(new AutocompleteItem(fullNameExtension + m.Name, + MakeAddition(m, false), MakeAddition(m, true), + EuclidTypeName(m.ReturnType), GetImageIndexFromType(m.ReturnType.Name))); + } + else if (m.IsGenericMethod || m.IsGenericMethodDefinition) { var sigWithTypes = m.GetSignature(); var nameAndAdditionWithTypes = sigWithTypes.Split('('); @@ -123,7 +132,9 @@ private static string MakeAddition(MethodInfo m, bool withType) { //var parameterName = parameters[i].Name + "1, " + parameters[i].Name + "2, ..."; addition += withType - ? TypeNameToAlias(parameters[i].ParameterType.Name.Replace("[]", "")) + " " + + ? (m.DeclaringType == typeof(MathNet.Numerics.Euclid) + ? EuclidTypeName(parameters[i].ParameterType.GetElementType()) + : TypeNameToAlias(parameters[i].ParameterType.Name.Replace("[]", ""))) + " " + parameters[i].Name + j + ", " : parameters[i].Name + j + ","; @@ -140,7 +151,8 @@ private static string MakeAddition(MethodInfo m, bool withType) parameterType = parameterType.GetElementType(); addition += withType - ? modifier + TypeNameToAlias(parameterType.Name) + " " + parameter.Name + ", " + ? modifier + (m.DeclaringType == typeof(MathNet.Numerics.Euclid) + ? EuclidTypeName(parameterType) : TypeNameToAlias(parameterType.Name)) + " " + parameter.Name + ", " : modifier + parameter.Name + ","; } } @@ -157,6 +169,18 @@ private static string MakeAddition(MethodInfo m, bool withType) return addition; } + private static string EuclidTypeName(Type type) + { + if (type == typeof(int)) + return "int"; + if (type.IsArray) + return EuclidTypeName(type.GetElementType()) + "[]"; + if (type.IsGenericType) + return type.Name.Split('`')[0] + "<" + + string.Join(", ", type.GetGenericArguments().Select(EuclidTypeName)) + ">"; + return TypeNameToAlias(type.Name); + } + private static void AddMetadata(MemberInfo p, Type type, List items) { diff --git a/Computator.NET.Core/Static/functions.xml b/Computator.NET.Core/Static/functions.xml index fb39be8b..e38ca9e1 100644 --- a/Computator.NET.Core/Static/functions.xml +++ b/Computator.NET.Core/Static/functions.xml @@ -5719,7 +5719,7 @@ Euclid Integer arithmetic -

integer Euclid.CeilingToPowerOfTwo(integer number)

+

int Euclid.CeilingToPowerOfTwo(int number)
integer Euclid.CeilingToPowerOfTwo(integer number)

Finds the smallest representable power of two at least as large as a positive integer.

Definition / convention: For number>0, result=2^ceil(log2(number)).

Arguments / value: Positive int up to 2^30 or positive integer (64-bit) up to 2^62.

@@ -5781,7 +5781,7 @@ Euclid Integer arithmetic -

BigInteger Euclid.GreatestCommonDivisor(IList`1 integers)
integer Euclid.GreatestCommonDivisor(IList`1 integers)

+

BigInteger Euclid.GreatestCommonDivisor(IList<BigInteger> integers)
integer Euclid.GreatestCommonDivisor(IList<integer> integers)

Greatest common divisor of integers

Finds the nonnegative common factor that divides every supplied integer.

Definition / convention: gcd(84,30)=6; gcd(a,b)=gcd(b,a remainder b).

@@ -5829,7 +5829,7 @@ Euclid Integer arithmetic -

bool Euclid.IsEven(integer number)

+

bool Euclid.IsEven(int number)
bool Euclid.IsEven(integer number)

Tests whether an integer is divisible by two.

Definition / convention: number=2*k for some integer k.

Arguments / value: Signed 32-bit or 64-bit integer.

@@ -5845,7 +5845,7 @@ Euclid Integer arithmetic -

bool Euclid.IsOdd(integer number)

+

bool Euclid.IsOdd(int number)
bool Euclid.IsOdd(integer number)

Tests whether an integer has an odd remainder on division by two.

Definition / convention: number=2*k+1 for some integer k.

Arguments / value: Signed 32-bit or 64-bit integer.

@@ -5861,7 +5861,7 @@ Euclid Integer arithmetic -

bool Euclid.IsPerfectSquare(integer number)

+

bool Euclid.IsPerfectSquare(int number)
bool Euclid.IsPerfectSquare(integer number)

Tests whether a nonnegative integer is the square of an integer.

Definition / convention: number=k*k for some integer k.

Arguments / value: Signed 32-bit or 64-bit integer.

@@ -5877,7 +5877,7 @@ Euclid Integer arithmetic -

bool Euclid.IsPowerOfTwo(integer number)

+

bool Euclid.IsPowerOfTwo(int number)
bool Euclid.IsPowerOfTwo(integer number)

Tests whether a positive integer contains exactly one set binary bit.

Definition / convention: number=2^k for an integer k>=0.

Arguments / value: Signed 32-bit or 64-bit integer.

@@ -5917,7 +5917,7 @@ Euclid Integer arithmetic -

BigInteger Euclid.LeastCommonMultiple(IList`1 integers)
integer Euclid.LeastCommonMultiple(IList`1 integers)

+

BigInteger Euclid.LeastCommonMultiple(IList<BigInteger> integers)
integer Euclid.LeastCommonMultiple(IList<integer> integers)

Least common multiple of integers

Finds the smallest positive common multiple of nonzero integers, with zero returned when any operand is zero.

Definition / convention: lcm(a,b)=|a/gcd(a,b) times b| for nonzero a,b.

@@ -5965,7 +5965,7 @@ Euclid Integer arithmetic -

integer Euclid.Log2(integer number)

+

int Euclid.Log2(int number)

Returns the position of the highest set bit of a positive 32-bit integer.

Definition / convention: For number>0, result=floor(log2(number)).

Arguments / value: A positive int (32-bit integer); this method has no 64-bit overload.

@@ -5981,7 +5981,7 @@ Euclid Integer arithmetic -

BigInteger Euclid.Modulus(BigInteger dividend, BigInteger divisor)
Single Euclid.Modulus(Single dividend, Single divisor)
integer Euclid.Modulus(integer dividend, integer divisor)
real Euclid.Modulus(real dividend, real divisor)

+

BigInteger Euclid.Modulus(BigInteger dividend, BigInteger divisor)
Single Euclid.Modulus(Single dividend, Single divisor)
int Euclid.Modulus(int dividend, int divisor)
integer Euclid.Modulus(integer dividend, integer divisor)
real Euclid.Modulus(real dividend, real divisor)

Returns the residue using the divisor-sign convention.

Definition / convention: Modulus(-7,3)=2; Modulus(7,-3)=-2.

Arguments / value: dividend and a nonzero divisor of matching supported numeric types; overloads accept int, integer, real, Single and BigInteger.

@@ -5997,7 +5997,7 @@ Euclid Integer arithmetic -

integer Euclid.PowerOfTwo(integer exponent)

+

int Euclid.PowerOfTwo(int exponent)
integer Euclid.PowerOfTwo(integer exponent)

Returns a representable positive integer power of two.

Definition / convention: result=2^exponent.

Arguments / value: The int overload accepts exponents 0..30; the integer (64-bit) overload accepts 0..62.

@@ -6013,7 +6013,7 @@ Euclid Integer arithmetic -

BigInteger Euclid.Remainder(BigInteger dividend, BigInteger divisor)
Single Euclid.Remainder(Single dividend, Single divisor)
integer Euclid.Remainder(integer dividend, integer divisor)
real Euclid.Remainder(real dividend, real divisor)

+

BigInteger Euclid.Remainder(BigInteger dividend, BigInteger divisor)
Single Euclid.Remainder(Single dividend, Single divisor)
int Euclid.Remainder(int dividend, int divisor)
integer Euclid.Remainder(integer dividend, integer divisor)
real Euclid.Remainder(real dividend, real divisor)

Returns the same remainder as the % operator.

Definition / convention: Remainder(-7,3)=-1; Remainder(7,-3)=1.

Arguments / value: dividend and a nonzero divisor of matching supported numeric types; overloads accept int, integer, real, Single and BigInteger.