diff --git a/src/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs b/src/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs index 5974b21203bc..dee591bb6c0a 100644 --- a/src/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs +++ b/src/Microsoft.VisualBasic.Core/ref/Microsoft.VisualBasic.Core.cs @@ -481,8 +481,11 @@ public sealed partial class Interaction internal Interaction() { } public static void Beep() { } public static object CallByName(object ObjectRef, string ProcName, CallType UseCallType, params object[] Args) { throw null; } + public static object Choose(double Index, params object[] Choice) { throw null; } public static object CreateObject(string ProgId, string ServerName = "") { throw null; } public static object IIf(bool Expression, object TruePart, object FalsePart) { throw null; } + public static string Partition(long Number, long Start, long Stop, long Interval) { throw null; } + public static object Switch(params object[] VarExpr) { throw null; } } public enum MsgBoxResult { diff --git a/src/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/Interaction.vb b/src/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/Interaction.vb index 2b67161968a8..88767abd6612 100644 --- a/src/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/Interaction.vb +++ b/src/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/Interaction.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System +Imports System.Text Imports System.Runtime.InteropServices Imports Microsoft.VisualBasic.CompilerServices @@ -25,6 +26,22 @@ Namespace Microsoft.VisualBasic #End If End Sub + '============================================================================ + ' String functions. + '============================================================================ + Public Function Choose(ByVal Index As Double, ByVal ParamArray Choice() As Object) As Object + + Dim FixedIndex As Integer = CInt(Fix(Index) - 1) 'ParamArray is 0 based, but Choose assumes 1 based + + If Choice.Rank <> 1 Then + Throw New ArgumentException(GetResourceString(SR.Argument_RankEQOne1, "Choice")) + ElseIf FixedIndex < 0 OrElse FixedIndex > Choice.GetUpperBound(0) Then + Return Nothing + End If + + Return Choice(FixedIndex) + End Function + Public Function IIf(ByVal Expression As Boolean, ByVal TruePart As Object, ByVal FalsePart As Object) As Object If Expression Then Return TruePart @@ -41,6 +58,143 @@ Namespace Microsoft.VisualBasic Return falsePart End Function + Public Function Partition(ByVal Number As Long, ByVal Start As Long, ByVal [Stop] As Long, ByVal Interval As Long) As String + 'CONSIDER: Change to use StringBuilder + Dim Lower As Long + Dim Upper As Long + Dim NoUpper As Boolean + Dim NoLower As Boolean + Dim Buffer As String = Nothing + Dim Buffer1 As String + Dim Buffer2 As String + Dim Spaces As Long + + 'Validate arguments + If Start < 0 Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Start")) + End If + + If [Stop] <= Start Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Stop")) + End If + + If Interval < 1 Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "Interval")) + End If + + 'Check for before-first and after-last ranges + If Number < Start Then + Upper = Start - 1 + NoLower = True + ElseIf Number > [Stop] Then + Lower = [Stop] + 1 + NoUpper = True + ElseIf Interval = 1 Then 'This is a special case + Lower = Number + Upper = Number + Else + 'Calculate the upper and lower ranges + 'Note the use of Integer division "\" which truncates to whole number + Lower = ((Number - Start) \ Interval) * Interval + Start + Upper = Lower + Interval - 1 + + 'Adjust for first and last ranges + If Upper > [Stop] Then + Upper = [Stop] + End If + + If Lower < Start Then + Lower = Start + End If + End If + + 'Build-up the string. Calculate number of spaces needed: VB3 uses Stop + 1. + 'This may seem bogus but it has to be this way for VB3 compatibilty. + Buffer1 = CStr([Stop] + 1) + Buffer2 = CStr(Start - 1) + + If Len(Buffer1) > Len(Buffer2) Then + Spaces = Len(Buffer1) + Else + Spaces = Len(Buffer2) + End If + + 'Handle case where Upper is -1 and Stop < 9 + If NoLower Then + Buffer1 = CStr(Upper) + If Spaces < Len(Buffer1) Then + Spaces = Len(Buffer1) + End If + End If + + 'Insert lower-end of partition range. + If NoLower Then + InsertSpaces(Buffer, Spaces) + Else + InsertNumber(Buffer, Lower, Spaces) + End If + + 'Insert the partition + Buffer = Buffer & ":" + + 'Insert upper-end of partition range + If NoUpper Then + InsertSpaces(Buffer, Spaces) + Else + InsertNumber(Buffer, Upper, Spaces) + End If + + Return Buffer + End Function + + Private Sub InsertSpaces(ByRef Buffer As String, ByVal Spaces As Long) + Do While Spaces > 0 'consider: - use stringbuilder + Buffer = Buffer & " " + Spaces = Spaces - 1 + Loop + End Sub + + Private Sub InsertNumber(ByRef Buffer As String, ByVal Num As Long, ByVal Spaces As Long) + Dim Buffer1 As String 'consider: - use stringbuilder + + 'Convert number to a string + Buffer1 = CStr(Num) + + 'Insert leading spaces + InsertSpaces(Buffer, Spaces - Len(Buffer1)) + + 'Append string + Buffer = Buffer & Buffer1 + End Sub + + Public Function Switch(ByVal ParamArray VarExpr() As Object) As Object + Dim Elements As Integer + Dim Index As Integer + + If VarExpr Is Nothing Then + Return Nothing + End If + + Elements = VarExpr.Length + Index = 0 + + 'Ensure we have an even number of arguments (0 based) + If (Elements Mod 2) <> 0 Then + Throw New ArgumentException(GetResourceString(SR.Argument_InvalidValue1, "VarExpr")) + End If + + Do While Elements > 0 + If CBool(VarExpr(Index)) Then + Return VarExpr(Index + 1) + End If + + Index += 2 + Elements -= 2 + Loop + + Return Nothing 'If nothing matched above + End Function + Public Function CreateObject(ByVal ProgId As String, Optional ByVal ServerName As String = "") As Object 'Creates local or remote COM2 objects. Should not be used to create COM+ objects. 'Applications that need to be STA should set STA either on their Sub Main via STAThreadAttribute diff --git a/src/Microsoft.VisualBasic.Core/tests/InteractionTests.cs b/src/Microsoft.VisualBasic.Core/tests/InteractionTests.cs index 5f540bfb089b..c3ff8f43b767 100644 --- a/src/Microsoft.VisualBasic.Core/tests/InteractionTests.cs +++ b/src/Microsoft.VisualBasic.Core/tests/InteractionTests.cs @@ -74,6 +74,22 @@ public object this[object index] } } + + [Fact] + public void Choose() + { + object[] x = { "Choice1", "Choice2", "Choice3", "Choice4", "Choice5", "Choice6" }; + Assert.Equal(null, Interaction.Choose(5)); + Assert.Equal(null, Interaction.Choose(0, x)); // < 1 + Assert.Equal(null, Interaction.Choose(x.Length + 1, x)); // > UpperBound + Assert.Equal(2, Interaction.Choose(2, 1, 2, 3)); + Assert.Equal("Choice3", Interaction.Choose(3, x[0], x[1], x[2])); + for (int i = 1; i <= x.Length; i++) + { + Assert.Equal(x[i - 1], Interaction.Choose(i, x)); + } + } + [Fact] public void CreateObject() { @@ -98,5 +114,69 @@ private static IEnumerable IIf_TestData() yield return new object[] { false, 3, "str", "str" }; yield return new object[] { true, 3, "str", 3 }; } + + [Theory] + [InlineData(0, 1, 2, 1, " :0")] + [InlineData(1, 1, 2, 1, "1:1")] + [InlineData(2, 1, 2, 1, "2:2")] + [InlineData(3, 1, 2, 1, "3: ")] + [InlineData(10, 1, 9, 1, "10: ")] + [InlineData(-1, 0, 1, 1, " :-1")] + [InlineData(-50, 0, 1, 1, " :-1")] + [InlineData(0, 1, 100, 10, " : 0")] + [InlineData(1, 1, 100, 10, " 1: 10")] + [InlineData(15, 1, 100, 10, " 11: 20")] + [InlineData(25, 1, 100, 10, " 21: 30")] + [InlineData(35, 1, 100, 10, " 31: 40")] + [InlineData(45, 1, 100, 10, " 41: 50")] + [InlineData(50, 40, 100, 10, " 50: 59")] + [InlineData(120, 100, 200, 10, "120:129")] + [InlineData(150, 100, 120, 10, "121: ")] + [InlineData(5001, 1, 10000, 100, " 5001: 5100")] + [InlineData(1, 0, 1, long.MaxValue, " 0: 1")] + [InlineData(1, 0, long.MaxValue - 1, long.MaxValue, " 0:9223372036854775806")] + [InlineData(long.MaxValue, 0, long.MaxValue - 1, 1, "9223372036854775807: ")] + [InlineData(long.MaxValue - 1, 0, long.MaxValue - 1, 1, "9223372036854775806:9223372036854775806")] + public void Partition(long Number, long Start, long Stop, long Interval, string expected) + { + Assert.Equal(expected, Interaction.Partition(Number, Start, Stop, Interval)); + } + + [Theory] + [InlineData(0, -1, 100, 10)] // Start < 0 + [InlineData(0, 100, 100, 10)] // Stop <= Start + [InlineData(0, 1, 100, 0)] // Interval < 1 + public void Partition_Invalid(long Number, long Start, long Stop, long Interval) + { + Assert.Throws(() => Interaction.Partition(Number, Start, Stop, Interval)); + } + + [Theory] + [InlineData(1, 0, long.MaxValue, 1)] // Stop + 1 + [InlineData(1, 0, long.MaxValue, long.MaxValue)] + [InlineData(2, 1, 2, long.MaxValue)] // Lower + Interval + [InlineData(long.MaxValue - 1, long.MaxValue - 1, long.MaxValue, 1)] + public void Partition_Overflow(long Number, long Start, long Stop, long Interval) + { + Assert.Throws(() => Interaction.Partition(Number, Start, Stop, Interval)); + } + + [Theory] + [InlineData(null, null)] // empty + [InlineData(new object[0], null)] // empty + [InlineData(new object[] { false, "red", false, "green", false, "blue" }, null)] // none + [InlineData(new object[] { true, "red", false, "green", false, "blue" }, "red")] + [InlineData(new object[] { false, "red", true, "green", false, "blue" }, "green")] + [InlineData(new object[] { false, "red", false, "green", true, "blue" }, "blue")] + public void Switch(object[] VarExpr, object expected) + { + Assert.Equal(expected, Interaction.Switch(VarExpr)); + } + + [Fact] + public void Switch_Invalid() + { + Assert.Throws(() => Interaction.Switch(true, "a", false)); + } } }