diff --git a/src/dev/impl/DevToys/DevToys.csproj b/src/dev/impl/DevToys/DevToys.csproj
index 2418f39824..a9a5499d15 100644
--- a/src/dev/impl/DevToys/DevToys.csproj
+++ b/src/dev/impl/DevToys/DevToys.csproj
@@ -24,6 +24,8 @@
+
+
@@ -31,6 +33,9 @@
+
+ GuidGeneratorToolPage.xaml
+
HtmlEncoderDecoderToolPage.xaml
@@ -198,6 +203,7 @@
+
@@ -224,6 +230,8 @@
+
+
@@ -294,8 +302,8 @@
Base64EncoderDecoderToolPage.xaml
-
- GuidGeneratorToolPage.xaml
+
+ NumberBaseConverterToolPage.xaml
HashGeneratorToolPage.xaml
@@ -354,7 +362,7 @@
Designer
-
+
MSBuild:Compile
Designer
@@ -395,6 +403,10 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
Designer
@@ -447,7 +459,6 @@
Designer
MSBuild:Compile
-
@@ -481,6 +492,9 @@
Windows Desktop Extensions for the UWP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Number Base Converter tool
+
+
+ Binary
+
+
+ Configuration
+
+
+ Decimal
+
+
+ Number Base Converter
+
+
+ Format number
+
+
+ Hexadecimal
+
+
+ Input
+
+
+ Binary
+
+
+ Decimal
+
+
+ Select which Input type you want to use
+
+
+ Hexadecimal
+
+
+ Octal
+
+
+ Input type
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw
new file mode 100644
index 0000000000..0a162a510e
--- /dev/null
+++ b/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Number Base Converter tool
+
+
+ Binary
+
+
+ Configuration
+
+
+ Decimal
+
+
+ Number Base Converter
+
+
+ Format number
+
+
+ Hexadecimal
+
+
+ Input
+
+
+ Binary
+
+
+ Decimal
+
+
+ Select which Input type you want to use
+
+
+ Hexadecimal
+
+
+ Octal
+
+
+ Input type
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw
new file mode 100644
index 0000000000..3c39690b06
--- /dev/null
+++ b/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Outil de conversion de base de nombres
+
+
+ Binaire
+
+
+ Configuration
+
+
+ Décimal
+
+
+ Convertisseur de base de nombres
+
+
+ Format du numéro
+
+
+ Hexadécimal
+
+
+ Entrée
+
+
+ Binaire
+
+
+ Décimal
+
+
+ Sélectionnez le type d'entrée que vous souhaitez utiliser
+
+
+ Hexadécimal
+
+
+ Octal
+
+
+ Type d'entrée
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw
new file mode 100644
index 0000000000..0a162a510e
--- /dev/null
+++ b/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Number Base Converter tool
+
+
+ Binary
+
+
+ Configuration
+
+
+ Decimal
+
+
+ Number Base Converter
+
+
+ Format number
+
+
+ Hexadecimal
+
+
+ Input
+
+
+ Binary
+
+
+ Decimal
+
+
+ Select which Input type you want to use
+
+
+ Hexadecimal
+
+
+ Octal
+
+
+ Input type
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw
new file mode 100644
index 0000000000..0a162a510e
--- /dev/null
+++ b/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Number Base Converter tool
+
+
+ Binary
+
+
+ Configuration
+
+
+ Decimal
+
+
+ Number Base Converter
+
+
+ Format number
+
+
+ Hexadecimal
+
+
+ Input
+
+
+ Binary
+
+
+ Decimal
+
+
+ Select which Input type you want to use
+
+
+ Hexadecimal
+
+
+ Octal
+
+
+ Input type
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw
new file mode 100644
index 0000000000..0a162a510e
--- /dev/null
+++ b/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Number Base Converter tool
+
+
+ Binary
+
+
+ Configuration
+
+
+ Decimal
+
+
+ Number Base Converter
+
+
+ Format number
+
+
+ Hexadecimal
+
+
+ Input
+
+
+ Binary
+
+
+ Decimal
+
+
+ Select which Input type you want to use
+
+
+ Hexadecimal
+
+
+ Octal
+
+
+ Input type
+
+
+ Octal
+
+
\ No newline at end of file
diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs
new file mode 100644
index 0000000000..77bed7568f
--- /dev/null
+++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs
@@ -0,0 +1,53 @@
+#nullable enable
+
+using System.Composition;
+using System.Threading.Tasks;
+using DevToys.Api.Tools;
+using DevToys.Core.Threading;
+using DevToys.Shared.Api.Core;
+using Windows.UI.Xaml.Controls;
+
+namespace DevToys.ViewModels.Tools.NumberBaseConverter
+{
+ [Export(typeof(IToolProvider))]
+ [Name("Number Base Converter")]
+ [ProtocolName("baseconverter")]
+ [Order(0)]
+ [CompactOverlaySize(width: 400, height: 500)]
+ internal sealed class NumberBaseConverterToolProvider : ToolProviderBase, IToolProvider
+ {
+ private readonly IMefProvider _mefProvider;
+
+ public string DisplayName => LanguageManager.Instance.NumberBaseConverter.DisplayName;
+
+ public string AccessibleName => LanguageManager.Instance.NumberBaseConverter.AccessibleName;
+
+
+ public object IconSource
+ => new TaskCompletionNotifier(
+ ThreadHelper.RunOnUIThreadAsync(() =>
+ {
+ return Task.FromResult(
+ new FontIcon
+ {
+ Glyph = "\uFE2C"
+ });
+ }));
+
+ [ImportingConstructor]
+ public NumberBaseConverterToolProvider(IMefProvider mefProvider)
+ {
+ _mefProvider = mefProvider;
+ }
+
+ public bool CanBeTreatedByTool(string data)
+ {
+ return false;
+ }
+
+ public IToolViewModel CreateTool()
+ {
+ return _mefProvider.Import();
+ }
+ }
+}
diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs
new file mode 100644
index 0000000000..bc95fdf1f6
--- /dev/null
+++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs
@@ -0,0 +1,243 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Composition;
+using System.Linq;
+using System.Threading.Tasks;
+using DevToys.Api.Core;
+using DevToys.Api.Core.Settings;
+using DevToys.Api.Tools;
+using DevToys.Core;
+using DevToys.Core.Formatter;
+using DevToys.Core.Threading;
+using DevToys.Models;
+using DevToys.Shared.Core.Threading;
+using DevToys.Views.Tools.NumberBaseConverter;
+using Microsoft.Toolkit.Mvvm.ComponentModel;
+
+namespace DevToys.ViewModels.Tools.NumberBaseConverter
+{
+ [Export(typeof(NumberBaseConverterToolViewModel))]
+ public sealed class NumberBaseConverterToolViewModel : ObservableRecipient, IToolViewModel
+ {
+ ///
+ /// Which format is the input number.
+ ///
+ private static readonly SettingDefinition BaseNumber
+ = new(
+ name: $"{nameof(NumberBaseConverterToolViewModel)}.{nameof(BaseNumber)}",
+ isRoaming: true,
+ defaultValue: Radix.Decimal);
+
+ ///
+ /// Whether the value should be formatted or not.
+ ///
+ private static readonly SettingDefinition Formatted
+ = new(
+ name: $"{nameof(NumberBaseConverterToolViewModel)}.{nameof(Formatted)}",
+ isRoaming: true,
+ defaultValue: true);
+
+ private readonly Queue _convertQueue = new();
+ private readonly ISettingsProvider _settingsProvider;
+ private readonly IMarketingService _marketingService;
+
+ private string? _inputValue;
+ private string? _octalValue;
+ private string? _binaryValue;
+ private string? _decimalValue;
+ private string? _hexadecimalValue;
+ private string? _infoBarMessage;
+ private bool _isInfoBarOpen;
+ private bool _conversionInProgress;
+ private bool _toolSuccessfullyWorked;
+
+ internal NumberBaseConverterStrings Strings => LanguageManager.Instance.NumberBaseConverter;
+
+ public Type View { get; } = typeof(NumberBaseConverterToolPage);
+
+ ///
+ /// Gets or sets the input text.
+ ///
+ internal string? InputValue
+ {
+ get => _inputValue;
+ set
+ {
+ SetProperty(ref _inputValue, value);
+ QueueFormatting();
+ }
+ }
+
+ internal string? OctalValue
+ {
+ get => _octalValue;
+ private set => SetProperty(ref _octalValue, value);
+ }
+
+ internal string? BinaryValue
+ {
+ get => _binaryValue;
+ private set => SetProperty(ref _binaryValue, value);
+ }
+
+ internal string? DecimalValue
+ {
+ get => _decimalValue;
+ private set => SetProperty(ref _decimalValue, value);
+ }
+
+ internal string? HexaDecimalValue
+ {
+ get => _hexadecimalValue;
+ private set => SetProperty(ref _hexadecimalValue, value);
+ }
+
+ internal bool IsInfoBarOpen
+ {
+ get => _isInfoBarOpen;
+ private set => SetProperty(ref _isInfoBarOpen, value);
+ }
+
+ internal string? InfoBarMessage
+ {
+ get => _infoBarMessage;
+ private set => SetProperty(ref _infoBarMessage, value);
+ }
+
+ internal NumberBaseFormat InputBaseNumber
+ {
+ get
+ {
+ Radix settingsValue = _settingsProvider.GetSetting(BaseNumber);
+ NumberBaseFormat? baseNumberFormat = BaseNumbers.FirstOrDefault(x => x.Value == settingsValue);
+ return baseNumberFormat ?? NumberBaseFormat.Decimal;
+ }
+ set
+ {
+ if (InputBaseNumber != value)
+ {
+ _settingsProvider.SetSetting(BaseNumber, value.Value);
+
+ if (InputBaseNumber == NumberBaseFormat.Octal)
+ {
+ InputValue = NumberBaseFormatter.RemoveFormatting(OctalValue).ToString();
+ }
+ else if (InputBaseNumber == NumberBaseFormat.Binary)
+ {
+ InputValue = NumberBaseFormatter.RemoveFormatting(BinaryValue).ToString();
+ }
+ else if (InputBaseNumber == NumberBaseFormat.Hexadecimal)
+ {
+ InputValue = NumberBaseFormatter.RemoveFormatting(HexaDecimalValue).ToString();
+ }
+ else
+ {
+ InputValue = NumberBaseFormatter.RemoveFormatting(DecimalValue).ToString();
+ }
+ OnPropertyChanged();
+ QueueFormatting();
+ }
+ }
+ }
+
+ ///
+ /// Get a list of supported BaseNumbers
+ ///
+ internal IReadOnlyList BaseNumbers = new ObservableCollection {
+ NumberBaseFormat.Octal,
+ NumberBaseFormat.Binary,
+ NumberBaseFormat.Decimal,
+ NumberBaseFormat.Hexadecimal,
+ };
+
+ internal bool IsFormatted
+ {
+ get => _settingsProvider.GetSetting(Formatted);
+ set
+ {
+ if (_settingsProvider.GetSetting(Formatted) != value)
+ {
+ _settingsProvider.SetSetting(Formatted, value);
+ OnPropertyChanged();
+ QueueFormatting();
+ }
+ }
+ }
+
+ [ImportingConstructor]
+ public NumberBaseConverterToolViewModel(ISettingsProvider settingsProvider, IMarketingService marketingService)
+ {
+ _settingsProvider = settingsProvider;
+ _marketingService = marketingService;
+ }
+
+ private void QueueFormatting()
+ {
+ _convertQueue.Enqueue(InputValue ?? string.Empty);
+ TreatQueueAsync().Forget();
+ }
+
+ private async Task TreatQueueAsync()
+ {
+ if (_conversionInProgress)
+ {
+ return;
+ }
+
+ _conversionInProgress = true;
+
+ await TaskScheduler.Default;
+
+ while (_convertQueue.TryDequeue(out string value))
+ {
+ string octalValue = string.Empty;
+ string binaryValue = string.Empty;
+ string decimalValue = string.Empty;
+ string hexaDecimalValue = string.Empty;
+ string infoBarMessage = string.Empty;
+ bool isInfoBarOpen = false;
+ try
+ {
+ long? baseValue = NumberBaseFormatter.StringToBase(value, InputBaseNumber);
+ if (baseValue != null)
+ {
+ octalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Octal, IsFormatted);
+ binaryValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Binary, IsFormatted);
+ decimalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Decimal, IsFormatted);
+ hexaDecimalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Hexadecimal, IsFormatted);
+ }
+ }
+ catch (OverflowException exception)
+ {
+ isInfoBarOpen = true;
+ infoBarMessage = exception.Message;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogFault("NumberBaseConverter", ex, $"Input base number: {InputBaseNumber}");
+ }
+
+ ThreadHelper.RunOnUIThreadAsync(ThreadPriority.Low, () =>
+ {
+ OctalValue = octalValue;
+ BinaryValue = binaryValue;
+ DecimalValue = decimalValue;
+ HexaDecimalValue = hexaDecimalValue;
+ InfoBarMessage = infoBarMessage;
+ IsInfoBarOpen = isInfoBarOpen;
+
+ if (!_toolSuccessfullyWorked)
+ {
+ _toolSuccessfullyWorked = true;
+ _marketingService.NotifyToolSuccessfullyWorked();
+ }
+ }).ForgetSafely();
+ }
+
+ _conversionInProgress = false;
+ }
+ }
+}
diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs
new file mode 100644
index 0000000000..a67ee65f23
--- /dev/null
+++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Globalization;
+using System.Text;
+using DevToys.Models;
+
+namespace DevToys.Core.Formatter
+{
+ internal static class NumberBaseFormatter
+ {
+ ///
+ /// Based on
+ /// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs
+ ///
+ /// Current value
+ /// Current base number
+ /// Value converted to
+ public static long? StringToBase(string value, NumberBaseFormat baseNumber)
+ {
+ if (value.Length <= 0)
+ {
+ return null;
+ }
+
+ int length = value.Length;
+ int index = 0;
+
+ Span unformattedValue = RemoveFormatting(value);
+
+ // Check for a sign
+ int sign = 1;
+ if (unformattedValue[index] == '-')
+ {
+ if (baseNumber != NumberBaseFormat.Decimal)
+ {
+ throw new ArgumentException($"Base {baseNumber} can't have a negative number");
+ }
+
+ sign = -1;
+ index++;
+ }
+ else if (unformattedValue[index] == '+')
+ {
+ index++;
+ }
+
+ long result = 0;
+ long maxVal;
+
+ // Allow all non-decimal numbers to set the sign bit.
+ if (baseNumber == NumberBaseFormat.Decimal)
+ {
+ maxVal = 0x7FFFFFFFFFFFFFFF / 10;
+
+ // Read all of the digits and convert to a number
+ while (index < length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current))
+ {
+ // Check for overflows - this is sufficient & correct.
+ if (result > maxVal || result < 0)
+ {
+ throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
+ }
+
+ result = result * baseNumber.BaseNumber + current;
+ index++;
+ }
+
+ if (result is < 0 and not 0x800000000000000)
+ {
+ throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
+ }
+ }
+ else
+ {
+ maxVal =
+ baseNumber.BaseNumber == 10 ? 0xfffffffffffffff / 10 :
+ baseNumber.BaseNumber == 16 ? 0xfffffffffffffff / 16 :
+ baseNumber.BaseNumber == 8 ? 0xfffffffffffffff / 8 :
+ 0xfffffffffffffff / 2;
+
+ // Read all of the digits and convert to a number
+ while (index < unformattedValue.Length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current))
+ {
+ // Check for overflows - this is sufficient & correct.
+ if (result > maxVal)
+ {
+ throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
+ }
+
+ long temp = result * baseNumber.BaseNumber + current;
+
+ if (temp < result) // this means overflow as well
+ {
+ throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})");
+ }
+
+ result = temp;
+ index++;
+ }
+ }
+
+ if (baseNumber == NumberBaseFormat.Decimal)
+ {
+ result *= sign;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Based on
+ /// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs
+ ///
+ /// Current number to convert
+ ///
+ /// Define if the number need to base formatted
+ ///
+ public static string LongToBase(long number, NumberBaseFormat baseNumber, bool isFormatted)
+ {
+ Span buffer = stackalloc char[67]; // Longest possible string length for an integer in binary notation with prefix
+
+ // If the number is negative, make it positive and remember the sign.
+ long ul;
+ bool isNegative = false;
+ if (number < 0)
+ {
+ isNegative = true;
+
+ // For base 10, write out -num, but other bases write out the
+ // 2's complement bit pattern
+ ul = (10 == baseNumber.BaseNumber) ? -number : number;
+ }
+ else
+ {
+ ul = number;
+ }
+
+ // Special case the 0.
+ int index;
+ if (0 == ul)
+ {
+ buffer[0] = '0';
+ index = 1;
+ }
+ else
+ {
+ index = 0;
+ for (int i = 0; i < buffer.Length; i++) // for loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks
+ {
+ long div = ul / baseNumber.BaseNumber; // TODO https://github.com/dotnet/runtime/issues/5213
+ int charVal = (int)(ul - (div * baseNumber.BaseNumber));
+ ul = div;
+
+ buffer[i] = (charVal < 10) ?
+ (char)(charVal + '0') :
+ (char)(charVal + 'a' - 10);
+
+ if (ul == 0)
+ {
+ index = i + 1;
+ break;
+ }
+ }
+ }
+
+ if (baseNumber == NumberBaseFormat.Decimal)
+ {
+ // If it was negative, append the sign.
+ if (isNegative)
+ {
+ buffer[index++] = '-';
+ }
+ }
+
+ var builder = new StringBuilder();
+
+ for (int builderIndex = --index; builderIndex >= 0; builderIndex--)
+ {
+ builder.Append(buffer[builderIndex]);
+ if (isFormatted && builderIndex != 0 && builderIndex % baseNumber.GroupSize == 0)
+ {
+ builder.Append(baseNumber.GroupSeparator);
+ }
+ }
+
+ // Add padding left for Binary Format
+ if (baseNumber == NumberBaseFormat.Binary)
+ {
+ int reminder = ++index % baseNumber.GroupSize;
+ for (int padIndex = 0; reminder != 0 && padIndex < baseNumber.GroupSize - reminder; padIndex++)
+ {
+ builder.Insert(0, '0');
+ }
+ }
+
+ return builder.ToString().ToUpperInvariant();
+ }
+
+ ///
+ /// Remove formatting (whitespace and Culture separator)
+ ///
+ ///
+ ///
+ public static Span RemoveFormatting(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return Span.Empty;
+ }
+
+ string currentCulture = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
+ Span values = new char[value.Length];
+ int valueIndex = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ if (!char.IsWhiteSpace(value[i]) && value[i] != Convert.ToChar(currentCulture))
+ {
+ values[valueIndex] = value[i];
+ valueIndex++;
+ }
+ }
+ return values;
+ }
+
+ private static bool IsDigit(char c, int radix, out int result)
+ {
+ int tmp;
+ if ((uint)(c - '0') <= 9)
+ {
+ result = tmp = c - '0';
+ }
+ else if ((uint)(c - 'A') <= 'Z' - 'A')
+ {
+ result = tmp = c - 'A' + 10;
+ }
+ else if ((uint)(c - 'a') <= 'z' - 'a')
+ {
+ result = tmp = c - 'a' + 10;
+ }
+ else
+ {
+ result = -1;
+ return false;
+ }
+
+ return tmp < radix;
+ }
+ }
+}
diff --git a/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs b/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs
new file mode 100644
index 0000000000..68dacc596b
--- /dev/null
+++ b/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs
@@ -0,0 +1,52 @@
+#nullable enable
+
+using DevToys.Api.Core.Navigation;
+using DevToys.Shared.Core;
+using DevToys.ViewModels.Tools.GuidGenerator;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Navigation;
+
+namespace DevToys.Views.Tools.GuidGenerator
+{
+ public sealed partial class GuidGeneratorToolPage : Page
+ {
+ public static readonly DependencyProperty ViewModelProperty
+ = DependencyProperty.Register(
+ nameof(ViewModel),
+ typeof(GuidGeneratorToolViewModel),
+ typeof(GuidGeneratorToolPage),
+ new PropertyMetadata(null));
+
+ ///
+ /// Gets the page's view model.
+ ///
+ public GuidGeneratorToolViewModel ViewModel
+ {
+ get => (GuidGeneratorToolViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ public GuidGeneratorToolPage()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ if (ViewModel is null)
+ {
+ var parameters = (NavigationParameter)e.Parameter;
+
+ // Set the view model
+ Assumes.NotNull(parameters.ViewModel, nameof(parameters.ViewModel));
+ ViewModel = (GuidGeneratorToolViewModel)parameters.ViewModel!;
+ DataContext = ViewModel;
+
+ ViewModel.OutputTextBox = OutputTextBox;
+ }
+
+ base.OnNavigatedTo(e);
+ }
+ }
+}
diff --git a/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml
new file mode 100644
index 0000000000..46054efa35
--- /dev/null
+++ b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs
new file mode 100644
index 0000000000..f5ac4ed19e
--- /dev/null
+++ b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs
@@ -0,0 +1,50 @@
+#nullable enable
+
+using DevToys.Api.Core.Navigation;
+using DevToys.Shared.Core;
+using DevToys.ViewModels.Tools.NumberBaseConverter;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Navigation;
+
+namespace DevToys.Views.Tools.NumberBaseConverter
+{
+ public sealed partial class NumberBaseConverterToolPage : Page
+ {
+ public static readonly DependencyProperty ViewModelProperty
+ = DependencyProperty.Register(
+ nameof(ViewModel),
+ typeof(NumberBaseConverterToolViewModel),
+ typeof(NumberBaseConverterToolPage),
+ new PropertyMetadata(null));
+
+ ///
+ /// Gets the page's view model.
+ ///
+ public NumberBaseConverterToolViewModel ViewModel
+ {
+ get => (NumberBaseConverterToolViewModel)GetValue(ViewModelProperty);
+ set => SetValue(ViewModelProperty, value);
+ }
+
+ public NumberBaseConverterToolPage()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ if (ViewModel is null)
+ {
+ var parameters = (NavigationParameter)e.Parameter;
+
+ // Set the view model
+ Assumes.NotNull(parameters.ViewModel, nameof(parameters.ViewModel));
+ ViewModel = (NumberBaseConverterToolViewModel)parameters.ViewModel!;
+ DataContext = ViewModel;
+ }
+
+ base.OnNavigatedTo(e);
+ }
+ }
+}
diff --git a/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs b/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs
new file mode 100644
index 0000000000..e4ec486e6f
--- /dev/null
+++ b/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs
@@ -0,0 +1,62 @@
+using DevToys.Core.Formatter;
+using DevToys.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace DevToys.Tests.Core.Formatter
+{
+ [TestClass]
+ public class BaseNumberFormatterTests
+ {
+ [DataTestMethod]
+ [DataRow("C6AEA155", 3333333333, false)]
+ [DataRow("C6AE A155", 3333333333, true)]
+ public void HexadecimalToDecimal(string input, long expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Hexadecimal));
+ }
+
+ [DataTestMethod]
+ [DataRow("30653520525", 3333333333, false)]
+ [DataRow("30 653 520 525", 3333333333, true)]
+ public void OctalToDecimal(string input, long expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Octal));
+ }
+
+ [DataTestMethod]
+ [DataRow("000101001101", 333, false)]
+ [DataRow("0001 0100 1101", 333, true)]
+ [DataRow("11000110101011101010000101010101", 3333333333, false)]
+ [DataRow("1100 0110 1010 1110 1010 0001 0101 0101", 3333333333, true)]
+ public void BinaryToDecimal(string input, long expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Binary));
+ }
+
+ [DataTestMethod]
+ [DataRow(3333333333, "C6AEA155", false)]
+ [DataRow(3333333333, "C6AE A155", true)]
+ public void DecimalToHexadecimal(long input, string expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Hexadecimal, isFormatted));
+ }
+
+ [DataTestMethod]
+ [DataRow(3333333333, "30653520525", false)]
+ [DataRow(3333333333, "30 653 520 525", true)]
+ public void DecimalToOctal(long input, string expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Octal, isFormatted));
+ }
+
+ [DataTestMethod]
+ [DataRow(333, "000101001101", false)]
+ [DataRow(333, "0001 0100 1101", true)]
+ [DataRow(3333333333, "11000110101011101010000101010101", false)]
+ [DataRow(3333333333, "1100 0110 1010 1110 1010 0001 0101 0101", true)]
+ public void DecimalToBinary(long input, string expectedResult, bool isFormatted)
+ {
+ Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Binary, isFormatted));
+ }
+ }
+}
diff --git a/src/tests/DevToys.Tests/DevToys.Tests.csproj b/src/tests/DevToys.Tests/DevToys.Tests.csproj
index 8b6221b0ae..9d4d9d9e41 100644
--- a/src/tests/DevToys.Tests/DevToys.Tests.csproj
+++ b/src/tests/DevToys.Tests/DevToys.Tests.csproj
@@ -44,6 +44,7 @@
+
@@ -88,7 +89,7 @@
- {E3E4E200-B380-4207-9A7E-4C9421904502}
+ {e3e4e200-b380-4207-9a7e-4c9421904502}
DevToys