diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..eba1110b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto \ No newline at end of file diff --git a/.github/workflows/Nightly.yml b/.github/workflows/Nightly.yml index 2e57d471..d5f2268b 100644 --- a/.github/workflows/Nightly.yml +++ b/.github/workflows/Nightly.yml @@ -7,6 +7,7 @@ jobs: runs-on: windows-latest permissions: contents: write + packages: write steps: - name: Update draft on GitHub Releases id: release_drafter diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index eecc9b16..2896da16 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -13,18 +13,19 @@ jobs: - uses: actions/setup-dotnet@main with: dotnet-version: '10.x' - workloads: maui-ios, maui-maccatalyst, maui-android, wasm-tools + workloads: maui, wasm-tools - name: Build GitHub Releases draft artifacts env: RELEASE_NOTES: | - # ${{ github.event.release.name }} + ${{ format('# {0}', github.event.release.name) }} ${{ github.event.release.body }} run: | # .NET Core MSBuild cannot parse , and ; correctly so we replace them with MSBuild XML escapes: https://github.com/dotnet/msbuild/issues/471#issuecomment-366268743 $env:RELEASE_NOTES = $env:RELEASE_NOTES -replace ',','%2C' -replace ';','%3B' - dotnet build -c Release -p:PackageReleaseNotes="$env:RELEASE_NOTES" -p:PackageVersion="${{ github.event.release.tag_name }}" + # For why Not.Uno.Example is used, see Nightly.yml + dotnet build -c Not.Uno.Example -p:PackageReleaseNotes="$env:RELEASE_NOTES" -p:PackageVersion="${{ github.event.release.tag_name }}" shell: pwsh - name: Upload to GitHub Releases uses: svenstaro/upload-release-action@v2 diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 99e9492e..eedff77b 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -17,15 +17,9 @@ jobs: # Check required Xcode version for latest "dotnet workload restore": https://github.com/dotnet/macios/releases xcode-version: '26.2' # If this is to be changed, also check macos-version in matrix.os above - uses: actions/setup-dotnet@main - if: matrix.os != 'ubuntu-latest' with: dotnet-version: '10.x' - workloads: maui, wasm-tools - - uses: actions/setup-dotnet@main - if: matrix.os == 'ubuntu-latest' - with: - dotnet-version: '10.x' - workloads: maui-android, wasm-tools + workloads: ${{ case(startsWith(matrix.os, 'ubuntu'), 'maui-android, wasm-tools', 'maui, wasm-tools') }} - uses: actions/checkout@v6 with: submodules: 'recursive' diff --git a/CSharpMath.Avalonia.Example/MainView.xaml.cs b/CSharpMath.Avalonia.Example/MainView.xaml.cs index ad439cbe..a15fc205 100644 --- a/CSharpMath.Avalonia.Example/MainView.xaml.cs +++ b/CSharpMath.Avalonia.Example/MainView.xaml.cs @@ -1,30 +1,30 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Avalonia.Styling; - -namespace CSharpMath.Avalonia.Example { - public class MainView : UserControl { - public MainView() { - InitializeComponent(); - - var light = this.Find("lightThemeRbn")!; - var dark = this.Find("darkThemeRbn")!; - if ((string)Application.Current!.ActualThemeVariant.Key == "Dark") - dark.IsChecked = true; - else light.IsChecked = true; - light.IsCheckedChanged += (sender, e) => { - Application.Current!.RequestedThemeVariant = - light.IsChecked == true ? ThemeVariant.Light : ThemeVariant.Dark; - }; - dark.IsCheckedChanged += (sender, e) => { - Application.Current!.RequestedThemeVariant = - dark.IsChecked == false ? ThemeVariant.Light : ThemeVariant.Dark; - }; - } - - private void InitializeComponent() { - AvaloniaXamlLoader.Load(this); - } - } +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +namespace CSharpMath.Avalonia.Example { + public class MainView : UserControl { + public MainView() { + InitializeComponent(); + + var light = this.Find("lightThemeRbn")!; + var dark = this.Find("darkThemeRbn")!; + if ((string)Application.Current!.ActualThemeVariant.Key == "Dark") + dark.IsChecked = true; + else light.IsChecked = true; + light.IsCheckedChanged += (sender, e) => { + Application.Current!.RequestedThemeVariant = + light.IsChecked == true ? ThemeVariant.Light : ThemeVariant.Dark; + }; + dark.IsCheckedChanged += (sender, e) => { + Application.Current!.RequestedThemeVariant = + dark.IsChecked == false ? ThemeVariant.Light : ThemeVariant.Dark; + }; + } + + private void InitializeComponent() { + AvaloniaXamlLoader.Load(this); + } + } } \ No newline at end of file diff --git a/CSharpMath.Evaluation.Tests/CSharpMath.Evaluation.Tests.csproj b/CSharpMath.Evaluation.Tests/CSharpMath.Evaluation.Tests.csproj index 60490903..d01ad121 100644 --- a/CSharpMath.Evaluation.Tests/CSharpMath.Evaluation.Tests.csproj +++ b/CSharpMath.Evaluation.Tests/CSharpMath.Evaluation.Tests.csproj @@ -1,9 +1,9 @@ - - - - CSharpMath.EvaluationTests - - - - + + + + CSharpMath.EvaluationTests + + + + \ No newline at end of file diff --git a/CSharpMath.Evaluation.Tests/EvaluationTests.cs b/CSharpMath.Evaluation.Tests/EvaluationTests.cs index a54906e7..254cd57c 100644 --- a/CSharpMath.Evaluation.Tests/EvaluationTests.cs +++ b/CSharpMath.Evaluation.Tests/EvaluationTests.cs @@ -1,1058 +1,1058 @@ -using System.Linq; -using System.Text.RegularExpressions; -using AngouriMath; -using Xunit; - -namespace CSharpMath.EvaluationTests { - using Atom; - - public class EvaluationTests { - internal static MathList ParseLaTeX(string latex) => - LaTeXParser.MathListFromLaTeX(latex).Match(list => list, e => throw new Xunit.Sdk.XunitException(e)); - static Evaluation.MathItem ParseMath(string latex) => - Evaluation.Evaluate(ParseLaTeX(latex)).Match(entity => entity, e => throw new Xunit.Sdk.XunitException(e)); - static void Test(string input, string converted, string? result) { - void Test(string input) { - var math = ParseMath(input); - Assert.NotNull(math); - Assert.Equal(converted, LaTeXParser.MathListToLaTeX(Evaluation.Visualize(math)).ToString()); - // TODO: Apparently adding this line causes a stack overflow when all tests are run, investigate later - // Assert.Equal(converted, LaTeXParser.MathListToLaTeX(Evaluation.Visualize(ParseMath(converted))).ToString()); // Double checking conversion - // Ensure that the converted entity is valid by simplifying it - if (result != null) - Assert.Equal(result, - LaTeXParser.MathListToLaTeX(Evaluation.Visualize(Assert.IsType(math).Content.Simplify())).ToString()); - else Assert.IsNotType(result); - } - Test(input); - // This regex balances (, [ and \{ with ), ] and \} into one group, then inserts \left and \right - // But does not do this for \sqrt's [ and ] - Test(Regex.Replace(input, @"(?\(|\[|\\\\{)|(?<-open>\)|\]|\\\\}))+(?(open)(?!)))(\)|\]|\\\\})", @"\left$1$2\right$3")); - } - [Theory] - [InlineData("1", "1", "1")] - [InlineData("01", "1", "1")] - [InlineData("10", "10", "10")] - [InlineData("010", "10", "10")] - [InlineData("1.", "1", "1")] - [InlineData("01.", "1", "1")] - [InlineData("1.0", "1", "1")] - [InlineData("01.0", "1", "1")] - [InlineData(".1", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData(".10", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData("1.1", @"\frac{11}{10}", @"\frac{11}{10}")] - [InlineData("01.1", @"\frac{11}{10}", @"\frac{11}{10}")] - [InlineData("1.10", @"\frac{11}{10}", @"\frac{11}{10}")] - [InlineData("01.10", @"\frac{11}{10}", @"\frac{11}{10}")] - [InlineData("0.1", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData("00.1", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData("0.10", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData("00.10", @"\frac{1}{10}", @"\frac{1}{10}")] - [InlineData("1234", "1234", "1234")] - [InlineData("0123456789", "123456789", "123456789")] - [InlineData("1234.", "1234", "1234")] - [InlineData(".5678", @"\frac{2839}{5000}", @"\frac{2839}{5000}")] - [InlineData(".9876543210", "0.9876543210", "0.9876543210")] - [InlineData("1234.5678", @"\frac{6172839}{5000}", @"\frac{6172839}{5000}")] - [InlineData(@"\infty", @"\infty ", @"\infty ")] - [InlineData(@"1_2", @"1", @"1")] - [InlineData(@"10_2", @"2", @"2")] - [InlineData(@"1._2", @"1", @"1")] - [InlineData(@"1.1_2", @"\frac{3}{2}", @"\frac{3}{2}")] - [InlineData(@".1_3", @"\frac{1}{3}", @"\frac{1}{3}")] - [InlineData(@"10_3", @"3", @"3")] - [InlineData(@"10.1_3", @"\frac{10}{3}", @"\frac{10}{3}")] - public void Numbers(string input, string converted, string output) => - Test(input, converted, output); - [Theory] - [InlineData("a", "a", "a")] - [InlineData("ab", @"ab", @"ab")] - [InlineData("abc", @"abc", @"abc")] - [InlineData("3a", @"3a", @"3a")] - [InlineData("3ab", @"3ab", @"3ab")] - [InlineData("3a3", @"3a\cdot 3", @"9a")] - [InlineData("3aa", @"3aa", @"3a^2")] - [InlineData(@"3\mathrm{aa}", @"3\mathrm{aa}", @"3\mathrm{aa}")] - [InlineData(@"3\mathrm{aa}a", @"3\mathrm{aa}a", @"3a\mathrm{aa}")] - [InlineData(@"3\mathrm{a}a", @"3aa", @"3a^2")] - [InlineData(@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - @"abcd\mathrm{e}fgh\cdot \mathrm{i}jklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - // i is considered as a number instead of a variable, unlike other alphabets, so it is sorted to the front - @"\mathrm{i}aAbBcCdD\mathrm{e}EfFgGhHIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ")] - [InlineData(@"\alpha\beta\gamma\delta\epsilon\varepsilon\zeta\eta\theta\iota\kappa\varkappa" + - @"\lambda\mu\nu\xi\omicron\pi\varpi\rho\varrho\sigma\varsigma\tau\upsilon\phi\varphi\chi" + - @"\psi\omega\Gamma\Delta\Theta\Lambda\Xi\Pi\Sigma\Upsilon\Phi\Psi\Omega", - @"\alpha \beta \gamma \delta \epsilon \varepsilon \zeta " + - @"\eta \theta \iota \kappa \varkappa \lambda \mu " + - @"\nu \xi \omicron \mathrm{\pi }\varpi \rho \varrho " + - @"\sigma \varsigma \tau \upsilon \phi \varphi \chi " + - @"\psi \omega \Gamma \Delta \Theta \Lambda \Xi " + - @"\Pi \Sigma \Upsilon \Phi \Psi \Omega ", - @"\alpha \beta \chi \delta \Delta \epsilon \eta " + - @"\gamma \Gamma \iota \kappa \lambda \Lambda \mu " + - @"\nu \omega \Omega \omicron \phi \Phi \mathrm{\pi }\Pi " + - @"\psi \Psi \rho \sigma \Sigma \tau \theta " + - @"\Theta \upsilon \Upsilon \varepsilon \varkappa \varphi " + - @"\varpi \varrho \varsigma \xi \Xi \zeta ")] - [InlineData(@"a_2", @"a_2", @"a_2")] - [InlineData(@"a_2+a_2", @"a_2+a_2", @"2a_2")] - [InlineData(@"a_{23}", @"a_{\mathrm{23}}", @"a_{\mathrm{23}}")] - [InlineData(@"\pi_a", @"\mathrm{\pi _{\mathnormal{a}}}", @"\mathrm{\pi _{\mathnormal{a}}}")] - [InlineData(@"\pi_\kappa", @"\mathrm{\pi _{\mathnormal{\kappa }}}", @"\mathrm{\pi _{\mathnormal{\kappa }}}")] - [InlineData(@"\pi_{\kappa23}", @"\mathrm{\pi _{\kappa 23}}", @"\mathrm{\pi _{\kappa 23}}")] - [InlineData(@"\pi_{1a\kappa23}", @"\mathrm{\pi _{1a\kappa 23}}", @"\mathrm{\pi _{1a\kappa 23}}")] - [InlineData(@"ii", @"\mathrm{i}\cdot \mathrm{i}", @"-1")] - [InlineData(@"i\infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] - [InlineData(@"\infty\infty", @"\infty \cdot \infty ", @"\infty ")] - [InlineData(@"\theta θ \mathrm{theta\ θ}", @"\theta \theta \theta \theta ", @"\theta ^4")] - [InlineData(@"\theta_\theta\theta_θθ_\mathrm{theta}\mathrm{theta_θ}\mathrm{θ}_{theta}", @"\theta _{\theta }\theta _{\theta }\theta _{\theta }\theta _{\theta }\theta _{\theta }", @"\theta _{\theta }^5")] - public void Variables(string input, string converted, string result) => Test(input, converted, result); - [Theory] - [InlineData("a + b", @"a+b", "a+b")] - [InlineData("a - b", @"a-b", "a-b")] - [InlineData(@"a\times b", @"ab", @"ab")] - [InlineData(@"a\cdot b", @"ab", @"ab")] - [InlineData(@"a / b", @"\frac{a}{b}", @"\frac{a}{b}")] - [InlineData(@"a\div b", @"\frac{a}{b}", @"\frac{a}{b}")] - [InlineData(@"\frac ab", @"\frac{a}{b}", @"\frac{a}{b}")] - [InlineData(@"a + b + c", @"a+b+c", "a+b+c")] - [InlineData(@"a + b - c", @"a+b-c", "a+b-c")] - [InlineData(@"a + b \times c", @"a+bc", @"a+bc")] - [InlineData(@"a + b \cdot c", @"a+bc", @"a+bc")] - [InlineData(@"a + b / c", @"a+\frac{b}{c}", @"a+\frac{b}{c}")] - [InlineData(@"a - b + c", @"a-b+c", "a-b+c")] - [InlineData(@"a - b - c", @"a-b-c", @"a-b-c")] - [InlineData(@"a - b \times c", @"a-bc", @"a-bc")] - [InlineData(@"a - b / c", @"a-\frac{b}{c}", @"a-\frac{b}{c}")] - [InlineData(@"a \times b + c", @"ab+c", @"ab+c")] - [InlineData(@"a \times b - c", @"ab-c", @"ab-c")] - [InlineData(@"a \times b \times c", @"abc", @"abc")] - [InlineData(@"a \times b / c", @"\frac{ab}{c}", @"\frac{ab}{c}")] - [InlineData(@"a \cdot b + c", @"ab+c", @"ab+c")] - [InlineData(@"a \cdot b - c", @"ab-c", @"ab-c")] - [InlineData(@"a \cdot b \times c", @"abc", @"abc")] - [InlineData(@"a \times b \cdot c", @"abc", @"abc")] - [InlineData(@"a \cdot b \cdot c", @"abc", @"abc")] - [InlineData(@"a \cdot bc", @"abc", @"abc")] - [InlineData(@"a \cdot b / c", @"\frac{ab}{c}", @"\frac{ab}{c}")] - [InlineData(@"a / b + c", @"\frac{a}{b}+c", @"\frac{a}{b}+c")] - [InlineData(@"a / b - c", @"\frac{a}{b}-c", @"\frac{a}{b}-c")] - [InlineData(@"a / b \times c", @"\frac{a}{b}\cdot c", @"\frac{a}{b}\cdot c")] - [InlineData(@"a / b \cdot c", @"\frac{a}{b}\cdot c", @"\frac{a}{b}\cdot c")] - [InlineData(@"a / b / c", @"\frac{\frac{a}{b}}{c}", @"\frac{a}{bc}")] - [InlineData(@"2+\frac ab", @"2+\frac{a}{b}", @"2+\frac{a}{b}")] - [InlineData(@"\frac ab+2", @"\frac{a}{b}+2", @"\frac{a}{b}+2")] - [InlineData(@"2-\frac ab", @"2-\frac{a}{b}", @"2-\frac{a}{b}")] - [InlineData(@"\frac ab-2", @"\frac{a}{b}-2", @"\frac{a}{b}-2")] - [InlineData(@"2\times\frac ab", @"2\cdot \frac{a}{b}", @"2\cdot \frac{a}{b}")] - [InlineData(@"2\cdot\frac ab", @"2\cdot \frac{a}{b}", @"2\cdot \frac{a}{b}")] - [InlineData(@"\frac ab\times 2", @"\frac{a}{b}\cdot 2", @"\frac{a}{b}\cdot 2")] - [InlineData(@"\frac ab\cdot 2", @"\frac{a}{b}\cdot 2", @"\frac{a}{b}\cdot 2")] - [InlineData(@"2/\frac ab", @"\frac{2}{\frac{a}{b}}", @"\frac{2b}{a}")] - [InlineData(@"\frac ab/2", @"\frac{\frac{a}{b}}{2}", @"\frac{a}{2b}")] - [InlineData(@"1+\mathrm{i}", @"1+\mathrm{i}", @"1+\mathrm{i}")] - [InlineData(@"1-\mathrm{i}", @"1-\mathrm{i}", @"1-\mathrm{i}")] - [InlineData(@"\mathrm{i}+1", @"\mathrm{i}+1", @"1+\mathrm{i}")] - [InlineData(@"\mathrm{i}-1", @"\mathrm{i}-1", @"-1+\mathrm{i}")] - [InlineData(@"\infty+1", @"\infty +1", @"\infty ")] - [InlineData(@"\infty+\mathrm{i}", @"\infty +\mathrm{i}", @"\infty +\mathrm{i}")] - [InlineData(@"\infty+\infty", @"\infty +\infty ", @"\infty ")] - [InlineData(@"\infty\times \infty", @"\infty \cdot \infty ", @"\infty ")] - [InlineData(@"\infty\cdot \infty", @"\infty \cdot \infty ", @"\infty ")] - [InlineData(@"\mathrm{i}\times \infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] - [InlineData(@"\mathrm{i}\cdot \infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] - [InlineData(@"\infty\times \mathrm{i}", @"\infty \cdot \mathrm{i}", @"\infty \mathrm{i}")] - [InlineData(@"\mathrm{i}\times \mathrm{i}", @"\mathrm{i}\cdot \mathrm{i}", @"-1")] - [InlineData(@"\frac00", @"\frac{0}{0}", @"\mathrm{undefined}")] - [InlineData(@"\frac xx", @"\frac{x}{x}", @"1\quad \mathrm{for}\quad x\neq 0")] - [InlineData(@"\frac xx+1", @"\frac{x}{x}+1", @"2\quad \mathrm{for}\quad x\neq 0")] - [InlineData(@"\frac0\infty", @"\frac{0}{\infty }", @"0")] - [InlineData(@"\frac2\infty", @"\frac{2}{\infty }", @"0")] - [InlineData(@"\frac{-2}\infty", @"\frac{-2}{\infty }", @"0")] - [InlineData(@"\frac20", @"\frac{2}{0}", @"\mathrm{undefined}")] - [InlineData(@"\frac{-2}0", @"\frac{-2}{0}", @"\mathrm{undefined}")] - [InlineData(@"\frac\infty0", @"\frac{\infty }{0}", @"\mathrm{undefined}")] - [InlineData(@"\frac{-\infty}0", @"\frac{-\infty }{0}", @"\mathrm{undefined}")] - [InlineData(@"\frac\infty\infty", @"\frac{\infty }{\infty }", @"\mathrm{undefined}")] - [InlineData(@"\frac{-\infty}{\infty}", @"\frac{-\infty }{\infty }", @"\mathrm{undefined}")] - [InlineData(@"a / bc", @"\frac{a}{bc}", @"\frac{a}{bc}")] - [InlineData(@"a / bc / d", @"\frac{\frac{a}{bc}}{d}", @"\frac{a}{bcd}")] - [InlineData(@"-2/\sin x/y", @"\frac{\frac{-2}{\sin \left( x\right) }}{y}", @"\frac{-2}{\sin \left( x\right) \cdot y}")] - public void BinaryOperators(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"+i", @"\mathrm{i}", @"\mathrm{i}")] - [InlineData(@"-i", @"-\mathrm{i}", @"-\mathrm{i}")] - [InlineData("+a", "a", "a")] - [InlineData("-a", "-a", "-a")] - [InlineData("++a", "a", "a")] - [InlineData("+-a", "-a", "-a")] - [InlineData("-+a", "-a", "-a")] - [InlineData("--a", @"-\left( -1\right) \cdot a", "a")] - [InlineData("+++a", "a", "a")] - [InlineData("---a", @"-\left( -1\right) \left( -1\right) \cdot a", "-a")] - [InlineData("a++a", "a+a", @"2a")] - [InlineData("a+-a", "a-a", "0")] - [InlineData("a-+a", "a-a", "0")] - [InlineData("a--a", @"a--a", @"2a")] - [InlineData("a+++a", "a+a", @"2a")] - [InlineData("a---a", @"a--\left( -1\right) \cdot a", "0")] - [InlineData(@"a\times+a", @"aa", "a^2")] - [InlineData(@"a\times-a", @"a\left( -1\right) \cdot a", "-a^2")] - [InlineData(@"+a\times+a", @"aa", "a^2")] - [InlineData(@"-a\times-a", @"-a\left( -1\right) \cdot a", "a^2")] - [InlineData("a/+a", @"\frac{a}{a}", @"1\quad \mathrm{for}\quad a\neq 0")] - [InlineData("a/-a", @"\frac{a}{-a}", @"-1\quad \mathrm{for}\quad a\neq 0")] - [InlineData("+a/+a", @"\frac{a}{a}", @"1\quad \mathrm{for}\quad a\neq 0")] - [InlineData("-a/-a", @"\frac{-a}{-a}", @"1\quad \mathrm{for}\quad a\neq 0")] - [InlineData(@"-2+-2+-2", @"-2-2-2", "-6")] - [InlineData(@"-2--2--2", @"-2--2--2", "2")] - [InlineData(@"-2\times -2\times -2", @"-2\left( -1\right) \cdot 2\left( -1\right) \cdot 2", "-8")] - [InlineData(@"-2/-2/-2", @"\frac{\frac{-2}{-2}}{-2}", @"\frac{-1}{2}")] - public void UnaryOperators(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"9\%", @"\frac{9}{100}", @"\frac{9}{100}")] - [InlineData(@"a\%", @"\frac{a}{100}", @"\frac{a}{100}")] - [InlineData(@"\pi\%", @"\frac{\mathrm{\pi }}{100}", @"\frac{\mathrm{\pi }}{100}")] - [InlineData(@"a\%\%", @"\frac{\frac{a}{100}}{100}", @"\frac{a}{10000}")] - [InlineData(@"9\%+3", @"\frac{9}{100}+3", @"\frac{309}{100}")] - [InlineData(@"-9\%+3", @"-\frac{9}{100}+3", @"\frac{291}{100}")] - [InlineData(@"2^2\%", @"\frac{2^2}{100}", @"\frac{1}{25}")] - [InlineData(@"2\%^2", @"\left( \frac{2}{100}\right) ^2", @"\frac{1}{2500}")] - [InlineData(@"2\%2", @"\frac{2}{100}\cdot 2", @"\frac{1}{25}")] - [InlineData(@"1+2\%^2", @"1+\left( \frac{2}{100}\right) ^2", @"\frac{2501}{2500}")] - [InlineData(@"9\degree", @"\frac{9\mathrm{\pi }}{180}", @"\frac{\mathrm{\pi }}{20}")] - [InlineData(@"a\degree", @"\frac{a\mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi }}{180}")] - [InlineData(@"\pi\degree", @"\frac{\mathrm{\pi }\cdot \mathrm{\pi }}{180}", @"\frac{\mathrm{\pi ^{\mathnormal{2}}}}{180}")] - [InlineData(@"a\%\degree", @"\frac{\frac{a}{100}\cdot \mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi }}{18000}")] - [InlineData(@"a\degree\degree", @"\frac{\frac{a\mathrm{\pi }}{180}\cdot \mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi ^{\mathnormal{2}}}}{32400}")] - [InlineData(@"9\degree+3", @"\frac{9\mathrm{\pi }}{180}+3", @"3+\frac{\mathrm{\pi }}{20}")] - [InlineData(@"-9\degree+3", @"-\frac{9\mathrm{\pi }}{180}+3", @"3+\frac{-1}{20}\cdot \mathrm{\pi }")] - [InlineData(@"2^2\degree", @"\frac{2^2\cdot \mathrm{\pi }}{180}", @"\frac{\mathrm{\pi }}{45}")] - [InlineData(@"2\degree^2", @"\left( \frac{2\mathrm{\pi }}{180}\right) ^2", @"\frac{\mathrm{\pi ^{\mathnormal{2}}}}{8100}")] - [InlineData(@"2\degree2", @"\frac{2\mathrm{\pi }}{180}\cdot 2", @"\frac{\mathrm{\pi }}{45}")] - [InlineData(@"1+2\degree^2", @"1+\left( \frac{2\mathrm{\pi }}{180}\right) ^2", @"1+\frac{\mathrm{\pi ^{\mathnormal{2}}}}{8100}")] - [InlineData(@"0!", @"0!", @"1")] - [InlineData(@"2.5!", @"\left( \frac{5}{2}\right) !", @"\frac{15}{8}\cdot \sqrt{\mathrm{\pi }}")] - [InlineData(@"-1!", @"-1!", @"-1")] - [InlineData(@"(-1)!", @"\left( -1\right) !", @"\mathrm{undefined}")] - [InlineData(@"(-1)!!", @"2^{\frac{-1}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }\left( -1\right) \cdot 1\right) }{4}}\cdot \left( \frac{-1}{2}\right) !", @"1")] - [InlineData(@"(-1)!!!", @"\left( 2^{\frac{-1}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }\left( -1\right) \cdot 1\right) }{4}}\cdot \left( \frac{-1}{2}\right) !\right) !", @"1")] - public void PostfixOperators(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData("0", "1")] - [InlineData("1", "1")] - [InlineData("2", "2")] - [InlineData("3", "3")] - [InlineData("30", "42849873690624000")] - [InlineData("31", "191898783962510625")] - [InlineData(@"\frac{25}{2}", @"\sqrt[4]{2}^{25}\sqrt[4]{\frac{2}{\mathrm{\pi }}}\cdot \left( \frac{25}{4}\right) !")] - public void DoubleFactorial(string x, string result) => Test($"{x}!!", $$$"""2^{\frac{{{{x}}}}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }{{{(x == @"\frac{25}{2}" ? "" : @"\cdot ")}}}{{{x}}}\right) }{4}}\cdot \left( \frac{{{{x}}}}{2}\right) !""", result); - [Theory] - [InlineData(@"2^2", "2^2", "4")] - [InlineData(@".2^2", @"\left( \frac{1}{5}\right) ^2", @"\frac{1}{25}")] - [InlineData(@"2.^2", "2^2", "4")] - [InlineData(@"2.1^2", @"\left( \frac{21}{10}\right) ^2", @"\frac{441}{100}")] - [InlineData(@"a^a", "a^a", "a^a")] - [InlineData(@"a^{a+b}", "a^{a+b}", "a^{a+b}")] - [InlineData(@"a^{-2}", "a^{-2}", @"\frac{1}{a^2}")] - [InlineData(@"2^{3^4}", "2^{3^4}", "2417851639229258349412352")] - [InlineData(@"4^{3^2}", "4^{3^2}", "262144")] - [InlineData(@"4^3+2", "4^3+2", "66")] - [InlineData(@"2+3^4", "2+3^4", "83")] - [InlineData(@"4^3\cdot2", @"4^3\cdot 2", "128")] - [InlineData(@"2\times3^4", @"2\cdot 3^4", "162")] - [InlineData(@"1/x", @"\frac{1}{x}", @"\frac{1}{x}")] - [InlineData(@"2/x", @"\frac{2}{x}", @"\frac{2}{x}")] - [InlineData(@"0^x", @"0^x", @"0\quad \mathrm{for}\quad \left( 1+\frac{1}{\operatorname{sgn} \left( x\right) ^2}\right) \cdot x>0")] - [InlineData(@"1^x", @"1^x", @"1")] - [InlineData(@"x^0", @"x^0", @"1\quad \mathrm{for}\quad x\neq 0")] - [InlineData(@"x^{-1}", @"x^{-1}", @"\frac{1}{x}")] - [InlineData(@"-i^{-1}", @"-\mathrm{i^{\mathnormal{-1}}}", @"\mathrm{i}")] - [InlineData(@"i^{-2}", @"\mathrm{i^{\mathnormal{-2}}}", @"-1")] - [InlineData(@"i^{-1}", @"\mathrm{i^{\mathnormal{-1}}}", @"-\mathrm{i}")] - [InlineData(@"i^0", @"\mathrm{i^{\mathnormal{0}}}", @"1")] - [InlineData(@"i^1", @"\mathrm{i^{\mathnormal{1}}}", @"\mathrm{i}")] - [InlineData(@"i^2", @"\mathrm{i^{\mathnormal{2}}}", @"-1")] - [InlineData(@"i^3", @"\mathrm{i^{\mathnormal{3}}}", @"-\mathrm{i}")] - [InlineData(@"i^4", @"\mathrm{i^{\mathnormal{4}}}", @"1")] - [InlineData(@"i^5", @"\mathrm{i^{\mathnormal{5}}}", @"\mathrm{i}")] - [InlineData(@"\mathrm{-i^{\mathit -1}}", @"-\mathrm{i^{\mathnormal{-1}}}", @"\mathrm{i}")] - [InlineData(@"\mathrm{i^{-2}}", @"\mathrm{i^{\mathnormal{-2}}}", @"-1")] - [InlineData(@"\mathrm i^{-1}", @"\mathrm{i^{\mathnormal{-1}}}", @"-\mathrm{i}")] - [InlineData(@"\mathrm{i^0}", @"\mathrm{i^{\mathnormal{0}}}", @"1")] - [InlineData(@"\mathrm{i^1}", @"\mathrm{i^{\mathnormal{1}}}", @"\mathrm{i}")] - [InlineData(@"\mathrm{i^{\mathnormal 2}}", @"\mathrm{i^{\mathnormal{2}}}", @"-1")] - [InlineData(@"\mathrm{i^3}", @"\mathrm{i^{\mathnormal{3}}}", @"-\mathrm{i}")] - [InlineData(@"\mathrm{i^{\mathnormal{4}}}", @"\mathrm{i^{\mathnormal{4}}}", @"1")] - [InlineData(@"\mathrm{i^5}", @"\mathrm{i^{\mathnormal{5}}}", @"\mathrm{i}")] - [InlineData(@"\mathrm{zz^2}", @"\mathrm{zz^{\mathnormal{2}}}", @"\mathrm{zz^{\mathnormal{2}}}")] - [InlineData(@"\mathrm{zz^{aa}}", @"\mathrm{zz^{aa}}", @"\mathrm{zz^{aa}}")] - [InlineData(@"\mathrm{zz^{aa\mathnormal{a}}}", @"\mathrm{zz^{aa\mathnormal{a}}}", @"\mathrm{zz^{\mathnormal{a}aa}}")] - [InlineData(@"\mathrm{zz^{aa\cdot aa}}", @"\mathrm{zz^{aa\mathnormal{\cdot }aa}}", @"\mathrm{zz^{aa^{\mathnormal{2}}}}")] - [InlineData(@"\mathrm{a^2a^2ab}", @"a^2a^2\cdot \mathrm{ab}", @"a^4\cdot \mathrm{ab}")] - [InlineData(@"\mathrm{a_2a_2ab}", @"a_2a_2\mathrm{ab}", @"a_2^2\cdot \mathrm{ab}")] - [InlineData(@"\mathrm{a^2_2a_2^2ab}", @"a_2^2a_2^2\cdot \mathrm{ab}", @"a_2^4\cdot \mathrm{ab}")] - [InlineData(@"\mathrm{ab^2ab_2ab}", @"\mathrm{ab^{\mathnormal{2}}}\cdot \mathrm{ab_{\mathnormal{2}}}\cdot \mathrm{ab}", @"\mathrm{ab^{\mathnormal{3}}}\cdot \mathrm{ab_{\mathnormal{2}}}")] - [InlineData(@"10^2", @"10^2", @"100")] - [InlineData(@".1^2", @"\left( \frac{1}{10}\right) ^2", @"\frac{1}{100}")] - [InlineData(@"10^x", @"10^x", @"10^x")] - [InlineData(@"{\frac 12}^4", @"\left( \frac{1}{2}\right) ^4", @"\frac{1}{16}")] - [InlineData(@"\sqrt2", @"\sqrt{2}", @"\sqrt{2}")] - [InlineData(@"\sqrt2^2", @"\left( \sqrt{2}\right) ^2", "2")] - [InlineData(@"\sqrt[3]2", @"2^{\frac{1}{3}}", @"\sqrt[3]{2}")] - [InlineData(@"\sqrt[3/2]2", @"2^{\frac{1}{\frac{3}{2}}}", @"\sqrt[3]{2}^2")] - [InlineData(@"\sqrt[3]2^3", @"\left( 2^{\frac{1}{3}}\right) ^3", "2")] - [InlineData(@"\sqrt[3]2^{1+1+1}", @"\left( 2^{\frac{1}{3}}\right) ^{1+1+1}", "2")] - [InlineData(@"\sqrt[1+1+1]2^{1+1+1}", @"\left( 2^{\frac{1}{1+1+1}}\right) ^{1+1+1}", "2")] - public void Exponents(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\sin x", @"\sin \left( x\right) ", @"\sin \left( x\right) ")] - [InlineData(@"\cos x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] - [InlineData(@"\tan x", @"\tan \left( x\right) ", @"\tan \left( x\right) ")] - [InlineData(@"\cot x", @"\cot \left( x\right) ", @"\cot \left( x\right) ")] - [InlineData(@"\sec x", @"\sec \left( x\right) ", @"\sec \left( x\right) ")] - [InlineData(@"\csc x", @"\csc \left( x\right) ", @"\csc \left( x\right) ")] - [InlineData(@"\arcsin x", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\arccos x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] - [InlineData(@"\arctan x", @"\arctan \left( x\right) ", @"\arctan \left( x\right) ")] - [InlineData(@"\arccot x", @"\arccot \left( x\right) ", @"\arccot \left( x\right) ")] - [InlineData(@"\arcsec x", @"\arcsec \left( x\right) ", @"\arcsec \left( x\right) ")] - [InlineData(@"\arccsc x", @"\arccsc \left( x\right) ", @"\arccsc \left( x\right) ")] - [InlineData(@"\ln x", @"\ln \left( x\right) ", @"\ln \left( x\right) ")] - [InlineData(@"\log x", @"\log \left( x\right) ", @"\log \left( x\right) ")] - [InlineData(@"\log_3 x", @"\log _3\left( x\right) ", @"\log _3\left( x\right) ")] - [InlineData(@"\log_{10} x", @"\log \left( x\right) ", @"\log \left( x\right) ")] - [InlineData(@"\log_e x", @"\ln \left( x\right) ", @"\ln \left( x\right) ")] - [InlineData(@"\ln x^2", @"\ln \left( x^2\right) ", @"\ln \left( x^2\right) ")] - [InlineData(@"\log x^2", @"\log \left( x^2\right) ", @"\log \left( x^2\right) ")] - [InlineData(@"\log_{10} x^2", @"\log \left( x^2\right) ", @"\log \left( x^2\right) ")] - [InlineData(@"\log_3 x^2", @"\log _3\left( x^2\right) ", @"\log _3\left( x^2\right) ")] - [InlineData(@"\log_e x^2", @"\ln \left( x^2\right) ", @"\ln \left( x^2\right) ")] - [InlineData(@"\ln x^{-1}", @"\ln \left( x^{-1}\right) ", @"\ln \left( \frac{1}{x}\right) ")] - [InlineData(@"\log x^{-1}", @"\log \left( x^{-1}\right) ", @"\log \left( \frac{1}{x}\right) ")] - [InlineData(@"\log_{10} x^{-1}", @"\log \left( x^{-1}\right) ", @"\log \left( \frac{1}{x}\right) ")] - [InlineData(@"\log_3 x^{-1}", @"\log _3\left( x^{-1}\right) ", @"\log _3\left( \frac{1}{x}\right) ")] - [InlineData(@"\log_e x^{-1}", @"\ln \left( x^{-1}\right) ", @"\ln \left( \frac{1}{x}\right) ")] - [InlineData(@"2\sin x", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] - [InlineData(@"\sin 2x", @"\sin \left( 2x\right) ", @"\sin \left( 2x\right) ")] - [InlineData(@"\sin x2", @"\sin \left( x\cdot 2\right) ", @"\sin \left( 2x\right) ")] - [InlineData(@"\sin x2^2", @"\sin \left( x\cdot 2^2\right) ", @"\sin \left( 4x\right) ")] - [InlineData(@"\sin xy", @"\sin \left( xy\right) ", @"\sin \left( xy\right) ")] - [InlineData(@"\sin \frac\pi2", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) ", @"1")] - [InlineData(@"\sin \frac\pi2+1", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) +1", @"2")] - [InlineData(@"\cos +x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] - [InlineData(@"\cos -x", @"\cos \left( -x\right) ", @"\cos \left( -x\right) ")] - [InlineData(@"\tan x\%", @"\tan \left( \frac{x}{100}\right) ", @"\tan \left( \frac{x}{100}\right) ")] - [InlineData(@"\tan x\%^2", @"\tan \left( \left( \frac{x}{100}\right) ^2\right) ", @"\tan \left( \left( \frac{x}{100}\right) ^2\right) ")] - [InlineData(@"\cot x\times y", @"\cot \left( x\right) \cdot y", @"\cot \left( x\right) \cdot y")] - [InlineData(@"\cot x/y", @"\frac{\cot \left( x\right) }{y}", @"\frac{\cot \left( x\right) }{y}")] - [InlineData(@"\cos \arccos x", @"\cos \left( \arccos \left( x\right) \right) ", @"x")] - [InlineData(@"\sin^2 x", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] - [InlineData(@"\sin^2 xy+\cos^2 yx", @"\sin \left( xy\right) ^2+\cos \left( yx\right) ^2", @"1")] - [InlineData(@"\log^2 x", @"\log \left( x\right) ^2", @"\log \left( x\right) ^2")] - [InlineData(@"\ln^2 x", @"\ln \left( x\right) ^2", @"\ln \left( x\right) ^2")] - [InlineData(@"\log_{10}^2 x", @"\log \left( x\right) ^2", @"\log \left( x\right) ^2")] - [InlineData(@"\log_3^2 x", @"\log _3\left( x\right) ^2", @"\log _3\left( x\right) ^2")] - public void Functions(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\sin^{-1} x", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\cos^{-1} x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] - [InlineData(@"\tan^{-1} x", @"\arctan \left( x\right) ", @"\arctan \left( x\right) ")] - [InlineData(@"\cot^{-1} x", @"\arccot \left( x\right) ", @"\arccot \left( x\right) ")] - [InlineData(@"\sec^{-1} x", @"\arcsec \left( x\right) ", @"\arcsec \left( x\right) ")] - [InlineData(@"\csc^{-1} x", @"\arccsc \left( x\right) ", @"\arccsc \left( x\right) ")] - [InlineData(@"\arcsin^{-1} x", @"\sin \left( x\right) ", @"\sin \left( x\right) ")] - [InlineData(@"\arccos^{-1} x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] - [InlineData(@"\arctan^{-1} x", @"\tan \left( x\right) ", @"\tan \left( x\right) ")] - [InlineData(@"\arccot^{-1} x", @"\cot \left( x\right) ", @"\cot \left( x\right) ")] - [InlineData(@"\arcsec^{-1} x", @"\sec \left( x\right) ", @"\sec \left( x\right) ")] - [InlineData(@"\arccsc^{-1} x", @"\csc \left( x\right) ", @"\csc \left( x\right) ")] - [InlineData(@"\ln^{-1} x", @"\mathrm{e^{\mathnormal{x}}}", @"\mathrm{e^{\mathnormal{x}}}")] - [InlineData(@"\log^{-1} x", @"10^x", @"10^x")] - [InlineData(@"\log_3^{-1} x", @"3^x", @"3^x")] - [InlineData(@"\log^{-1}_{10} x", @"10^x", @"10^x")] - [InlineData(@"\log_e^{-1} x", @"\mathrm{e^{\mathnormal{x}}}", @"\mathrm{e^{\mathnormal{x}}}")] - [InlineData(@"\ln^{-1} x^2", @"\mathrm{e^{\mathnormal{x^2}}}", @"\mathrm{e^{\mathnormal{x^2}}}")] - [InlineData(@"\log^{-1} x^2", @"10^{x^2}", @"10^{x^2}")] - [InlineData(@"\log_{10}^{-1} x^2", @"10^{x^2}", @"10^{x^2}")] - [InlineData(@"\log_3^{-1} x^2", @"3^{x^2}", @"3^{x^2}")] - [InlineData(@"\log_e^{-1} x^2", @"\mathrm{e^{\mathnormal{x^2}}}", @"\mathrm{e^{\mathnormal{x^2}}}")] - [InlineData(@"\ln^{-1} x^{-1}", @"\mathrm{e^{\mathnormal{x^{-1}}}}", @"\mathrm{e^{\mathnormal{\frac{1}{x}}}}")] - [InlineData(@"\log^{-1} x^{-1}", @"10^{x^{-1}}", @"10^{\frac{1}{x}}")] - [InlineData(@"\log_{10}^{-1} x^{-1}", @"10^{x^{-1}}", @"10^{\frac{1}{x}}")] - [InlineData(@"\log_3^{-1} x^{-1}", @"3^{x^{-1}}", @"3^{\frac{1}{x}}")] - [InlineData(@"\log_e^{-1} x^{-1}", @"\mathrm{e^{\mathnormal{x^{-1}}}}", @"\mathrm{e^{\mathnormal{\frac{1}{x}}}}")] - [InlineData(@"2\sin^{-1} x", @"2\arcsin \left( x\right) ", @"2\arcsin \left( x\right) ")] - [InlineData(@"\sin^{-1} 2x", @"\arcsin \left( 2x\right) ", @"\arcsin \left( 2x\right) ")] - [InlineData(@"\sin^{-1} xy", @"\arcsin \left( xy\right) ", @"\arcsin \left( xy\right) ")] - [InlineData(@"\cos^{-1} +x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] - [InlineData(@"\cos^{-1} -x", @"\arccos \left( -x\right) ", @"\arccos \left( -x\right) ")] - [InlineData(@"\tan^{-1} x\%", @"\arctan \left( \frac{x}{100}\right) ", @"\arctan \left( \frac{x}{100}\right) ")] - [InlineData(@"\tan^{-1} x\%^2", @"\arctan \left( \left( \frac{x}{100}\right) ^2\right) ", @"\arctan \left( \left( \frac{x}{100}\right) ^2\right) ")] - [InlineData(@"\cot^{-1} x\times y", @"\arccot \left( x\right) \cdot y", @"\arccot \left( x\right) \cdot y")] - [InlineData(@"\cot^{-1} x/y", @"\frac{\arccot \left( x\right) }{y}", @"\frac{\arccot \left( x\right) }{y}")] - [InlineData(@"\cos^{-1} \arccos^{-1} x", @"\arccos \left( \cos \left( x\right) \right) ", @"x")] - [InlineData(@"\sin^1 x", @"\sin \left( x\right) ^1", @"\sin \left( x\right) ")] - [InlineData(@"\sin^{+1} x", @"\sin \left( x\right) ^1", @"\sin \left( x\right) ")] - [InlineData(@"\sin^{+-1} x", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin^{-+1} x", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin^{--1} x", @"\sin \left( x\right) ^{-\left( -1\right) \cdot 1}", @"\sin \left( x\right) ")] - [InlineData(@"\sin^{-1^2} x", @"\sin \left( x\right) ^{-1^2}", @"\csc \left( x\right) ")] - [InlineData(@"\sin^{-1+3} xy+\cos^{-1+3} yx", @"\sin \left( xy\right) ^{-1+3}+\cos \left( yx\right) ^{-1+3}", @"1")] - [InlineData(@"\log^{-a_2} x", @"\log \left( x\right) ^{-a_2}", @"\log \left( x\right) ^{-a_2}")] - [InlineData(@"\ln^{3-1} x", @"\ln \left( x\right) ^{3-1}", @"\ln \left( x\right) ^2")] - public void FunctionInverses(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"1+(2+3)", @"1+2+3", @"6")] - [InlineData(@"1+((2+3))", @"1+2+3", @"6")] - [InlineData(@"2\times(3+4)", @"2\left( 3+4\right) ", @"14")] - [InlineData(@"(3+4)\times2", @"\left( 3+4\right) \cdot 2", @"14")] - [InlineData(@"(5+6)^2", @"\left( 5+6\right) ^2", @"121")] - [InlineData(@"(5+6)", @"5+6", @"11")] - [InlineData(@"((5+6))", @"5+6", @"11")] - [InlineData(@"(5+6)2", @"\left( 5+6\right) \cdot 2", @"22")] - [InlineData(@"2(5+6)", @"2\left( 5+6\right) ", @"22")] - [InlineData(@"2(5+6)2", @"2\left( 5+6\right) \cdot 2", @"44")] - [InlineData(@"(5+6)x", @"\left( 5+6\right) \cdot x", @"11x")] - [InlineData(@"x(5+6)", @"x\left( 5+6\right) ", @"11x")] - [InlineData(@"x(5+6)x", @"x\left( 5+6\right) \cdot x", @"11x^2")] - [InlineData(@"(5+6).2", @"\left( 5+6\right) \cdot \frac{1}{5}", @"\frac{11}{5}")] - [InlineData(@".2(5+6)", @"\frac{1}{5}\left( 5+6\right) ", @"\frac{11}{5}")] - [InlineData(@".2(5+6).2", @"\frac{1}{5}\left( 5+6\right) \cdot \frac{1}{5}", @"\frac{11}{25}")] - [InlineData(@"(5+6)2.", @"\left( 5+6\right) \cdot 2", @"22")] - [InlineData(@"2.(5+6)", @"2\left( 5+6\right) ", @"22")] - [InlineData(@"2.(5+6)2.", @"2\left( 5+6\right) \cdot 2", @"44")] - [InlineData(@"(5+6)(2)", @"\left( 5+6\right) \cdot 2", @"22")] - [InlineData(@"(5+6)(1+1)", @"\left( 5+6\right) \left( 1+1\right) ", @"22")] - [InlineData(@"(5+6)(-(-2))", @"\left( 5+6\right) \left( -1\right) \left( -1\right) \cdot 2", @"22")] - [InlineData(@"(5+6)(--2)", @"\left( 5+6\right) \left( -1\right) \left( -1\right) \cdot 2", @"22")] - [InlineData(@"+(1)", @"1", @"1")] - [InlineData(@"+(1)\%", @"\frac{1}{100}", @"\frac{1}{100}")] - [InlineData(@"+(-1)", @"-1", @"-1")] - [InlineData(@"-(+1)", @"-1", @"-1")] - [InlineData(@"-(-1)", @"-\left( -1\right) \cdot 1", @"1")] - [InlineData(@"--(--1)", @"-\left( -1\right) \left( -1\right) \left( -1\right) \cdot 1", @"1")] - [InlineData(@"(2+3)^{(4+5)}", @"\left( 2+3\right) ^{4+5}", @"1953125")] - [InlineData(@"(2+3)^{((4)+5)}", @"\left( 2+3\right) ^{4+5}", @"1953125")] - [InlineData(@"(1+i)\infty", @"\left( 1+\mathrm{i}\right) \cdot \infty ", @"\infty +\infty \mathrm{i}")] - [InlineData(@"2\sin(x)", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] - [InlineData(@"(2)\sin(x)", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] - [InlineData(@"\sin(x+1)", @"\sin \left( x+1\right) ", @"\sin \left( 1+x\right) ")] - [InlineData(@"\sin((x+1))", @"\sin \left( x+1\right) ", @"\sin \left( 1+x\right) ")] - [InlineData(@"\sin(2(x+1))", @"\sin \left( 2\left( x+1\right) \right) ", @"\sin \left( 2\left( 1+x\right) \right) ")] - [InlineData(@"\sin((x+1)+2)", @"\sin \left( x+1+2\right) ", @"\sin \left( 3+x\right) ")] - [InlineData(@"\sin(x)2", @"\sin \left( x\right) \cdot 2", @"2\sin \left( x\right) ")] - [InlineData(@"\sin(x)(x+1)", @"\sin \left( x\right) \left( x+1\right) ", @"\sin \left( x\right) \left( 1+x\right) ")] - [InlineData(@"\sin(x)(x+1)(x)", @"\sin \left( x\right) \left( x+1\right) \cdot x", @"\sin \left( x\right) \left( 1+x\right) \cdot x")] - [InlineData(@"\sin(x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] - [InlineData(@"\sin\ (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] - [InlineData(@"\sin\; (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] - [InlineData(@"\sin\ \; (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] - [InlineData(@"\sin^3(x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] - [InlineData(@"\sin^3\ (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] - [InlineData(@"\sin^3\; (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] - [InlineData(@"\sin^3\ \; (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] - [InlineData(@"\sin^{-1}(x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\sin^{-1}\ (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\sin^{-1}\; (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\sin^{-1}\ \; (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] - [InlineData(@"\sin(x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] - [InlineData(@"\sin\ (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] - [InlineData(@"\sin\; (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] - [InlineData(@"\sin\ \; (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] - [InlineData(@"\sin(x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin\ (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin\; (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin\ \; (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] - [InlineData(@"\sin^3(x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] - [InlineData(@"\sin^3\ (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] - [InlineData(@"\sin^3\; (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] - [InlineData(@"\sin^3\ \; (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] - [InlineData(@"\sin^{-1}(x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] - [InlineData(@"\sin^{-1}\ (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] - [InlineData(@"\sin^{-1}\; (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] - [InlineData(@"\sin^{-1}\ \; (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] - [InlineData(@"\sin^a(x)", @"\sin \left( x\right) ^a", @"\sin \left( x\right) ^a")] - [InlineData(@"\sin^a(x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] - [InlineData(@"\sin^a\ (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] - [InlineData(@"\sin^a\; (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] - [InlineData(@"\sin^a\ \; (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] - [InlineData(@"\sin(x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] - [InlineData(@"\sin\ (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] - [InlineData(@"\sin\; (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] - [InlineData(@"\sin\ \; (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] - [InlineData(@"\sin^a(x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] - [InlineData(@"\sin^a\ (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] - [InlineData(@"\sin^a\; (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] - [InlineData(@"\sin^a\ \; (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] - [InlineData(@"\sin (\frac\pi2)", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) ", @"1")] - [InlineData(@"\sin (\frac\pi2)+1", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) +1", @"2")] - public void Parentheses(string latex, string converted, string result) { - Test(latex, converted, result); - Test(latex.Replace('(', '[').Replace(')', ']'), converted, result); - } - [Theory] - [InlineData(@"\begin{matrix}1\end{matrix}", @"\left[ \begin{matrix}1\end{matrix}\right] ", @"1")] - [InlineData(@"\begin{matrix}1&2\\3&4\end{matrix}", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ")] - [InlineData(@"(\begin{matrix}1\end{matrix})^2", @"\left[ \begin{matrix}1\end{matrix}\right] ^2", @"1")] - [InlineData(@"[\begin{matrix}1&2\end{matrix}]^2", @"\left[ \begin{matrix}1&2\end{matrix}\right] ^2", @"\left[ \begin{matrix}1&4\end{matrix}\right] ")] - [InlineData(@"[\begin{matrix}3\\4\end{matrix}]^2", @"\left[ \begin{matrix}3\\ 4\end{matrix}\right] ^2", @"\left[ \begin{matrix}9\\ 16\end{matrix}\right] ")] - [InlineData(@"[\begin{matrix}1&2\\3&4\end{matrix}]^2", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ^2", @"\left[ \begin{matrix}7&10\\ 15&22\end{matrix}\right] ")] - [InlineData(@"\begin{matrix}1&2\\3&4\end{matrix}+\begin{matrix}1&2\\3&5\end{matrix}", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] +\left[ \begin{matrix}1&2\\ 3&5\end{matrix}\right] ", @"\left[ \begin{matrix}2&4\\ 6&9\end{matrix}\right] ")] - public void Matrices(string latex, string converted, string result) { - Test(latex, converted, result); - Test(latex.Replace("matrix", "pmatrix"), converted, result); - Test(latex.Replace("matrix", "bmatrix"), converted, result); - } - [Theory] - [InlineData(@"1,2", @"1,2")] - [InlineData(@"1,2,3", @"1,2,3")] - [InlineData(@"a,b,c,d", @"a,b,c,d")] - [InlineData(@"\sqrt2,\sqrt[3]2,\frac34", @"\sqrt{2},2^{\frac{1}{3}},\frac{3}{4}")] - [InlineData(@"\sin a,\cos b^2,\tan c_3,\cot de,\sec 12f,\csc g+h", - @"\sin \left( a\right) ,\cos \left( b^2\right) ,\tan \left( c_3\right) ,\cot \left( d\mathrm{e}\right) ,\sec \left( 12f\right) ,\csc \left( g\right) +h")] - public void Comma(string latex, string converted) => Test(latex, converted, null); - [Theory] - [InlineData(@"\emptyset", @"\emptyset ")] - [InlineData(@"\mathbb Z", @"\mathbb{Z}")] - [InlineData(@"\mathbb Q", @"\mathbb{Q}")] - [InlineData(@"\mathbb R", @"\mathbb{R}")] - [InlineData(@"\mathbb C", @"\mathbb{C}")] - [InlineData(@"\{\}", @"\emptyset ")] - [InlineData(@"\left\{\right\}", @"\emptyset ")] - [InlineData(@"\left\{1\right\}", @"\left\{ 1\right\} ")] - [InlineData(@"\{1\}", @"\left\{ 1\right\} ")] - [InlineData(@"\{1,2\}", @"\left\{ 1,2\right\} ")] - [InlineData(@"\{x,y\}", @"\left\{ x,y\right\} ")] - [InlineData(@"\{\sqrt[3]2,\frac34,\sin^2x\}", @"\left\{ 2^{\frac{1}{3}},\frac{3}{4},\sin \left( x\right) ^2\right\} ")] - [InlineData(@"\{\{\}\}", @"\left\{ \emptyset \right\} ")] - [InlineData(@"\{\left\{\right\}\}", @"\left\{ \emptyset \right\} ")] - public void Sets(string latex, string converted) => Test(latex, converted, null); - [Theory] - [InlineData(@"\emptyset\cup\{2\}", @"\emptyset \cup \left\{ 2\right\} ", @"\left\{ 2\right\} ")] - [InlineData(@"\{1\}\cup\{2\}", @"\left\{ 1\right\} \cup \left\{ 2\right\} ", @"\left\{ 1,2\right\} ")] - [InlineData(@"\{3,4\}\cap\emptyset", @"\left\{ 3,4\right\} \cap \emptyset ", @"\emptyset ")] - [InlineData(@"\{3,4\}\cap\{4,5\}", @"\left\{ 3,4\right\} \cap \left\{ 4,5\right\} ", @"\left\{ 4\right\} ")] - [InlineData(@"\{2,3,4\}\setminus\{4\}", @"\left\{ 2,3,4\right\} \setminus \left\{ 4\right\} ", @"\left\{ 2,3\right\} ")] - [InlineData(@"\emptyset^\complement", @"\mathbb{C}\setminus \emptyset ", @"\mathbb{C}")] - [InlineData(@"\{3\}^\complement", @"\mathbb{C}\setminus \left\{ 3\right\} ", @"\mathbb{C}\setminus \left\{ 3\right\} ")] - [InlineData(@"\mathbb R+1", @"\mathbb{R}+1", @"\mathbb{R}+1")] - [InlineData(@"\sin^\complement x", @"\mathbb{C}\setminus \sin \left( x\right) ", @"\mathbb{C}\setminus \sin \left( x\right) ")] - [InlineData(@"1^\complement", @"\mathbb{C}\setminus 1", @"\mathbb{C}\setminus 1")] - public void SetOperations(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"(1,2)", @"\left( 1,2\right) ", @"\left( 1,2\right) ")] - [InlineData(@"[1,2)", @"\left[ 1,2\right) ", @"\left[ 1,2\right) ")] - [InlineData(@"(1,2]", @"\left( 1,2\right] ", @"\left( 1,2\right] ")] - [InlineData(@"[1,2]", @"\left[ 1,2\right] ", @"\left[ 1,2\right] ")] - [InlineData(@"(2,1)", @"\left( 2,1\right) ", @"\left( 2,1\right) ")] - [InlineData(@"[2,1)", @"\left[ 2,1\right) ", @"\left[ 2,1\right) ")] - [InlineData(@"(2,1]", @"\left( 2,1\right] ", @"\left( 2,1\right] ")] - [InlineData(@"[2,1]", @"\left[ 2,1\right] ", @"\left[ 2,1\right] ")] - [InlineData(@"[1,2]\setminus\{1\}", @"\left[ 1,2\right] \setminus \left\{ 1\right\} ", @"\left[ 1,2\right] \setminus \left\{ 1\right\} ")] - [InlineData(@"[1,2]\setminus\{2\}", @"\left[ 1,2\right] \setminus \left\{ 2\right\} ", @"\left[ 1,2\right] \setminus \left\{ 2\right\} ")] - [InlineData(@"[1,2]\cup(2,3)", @"\left[ 1,2\right] \cup \left( 2,3\right) ", @"\left[ 1,3\right) ")] - [InlineData(@"[1,2]\cap[1.5,1.6]", @"\left[ 1,2\right] \cap \left[ \frac{3}{2},\frac{8}{5}\right] ", @"\left[ \frac{3}{2},\frac{8}{5}\right] ")] - [InlineData(@"[1.5,1.5]", @"\left\{ \frac{3}{2}\right\} ", @"\left\{ \frac{3}{2}\right\} ")] - [InlineData(@"(1.5,1.5]", @"\emptyset ", @"\emptyset ")] - [InlineData(@"[1.5,1.5)", @"\emptyset ", @"\emptyset ")] - [InlineData(@"(1.5,1.5)", @"\emptyset ", @"\emptyset ")] - [InlineData(@"[xy,xy]", @"\left\{ xy\right\} ", @"\left\{ xy\right\} ")] - [InlineData(@"(xy,xy]", @"\emptyset ", @"\emptyset ")] - [InlineData(@"[xy,xy)", @"\emptyset ", @"\emptyset ")] - [InlineData(@"(xy,xy)", @"\emptyset ", @"\emptyset ")] - [InlineData(@"\frac{[7, 8]}{9}", @"\frac{\left[ 7,8\right] }{9}", @"\frac{\left[ 7,8\right] }{9}")] - [InlineData(@"+(3,4]", @"\left( 3,4\right] ", @"\left( 3,4\right] ")] - public void Intervals(string latex, string converted, string result) { - Test(latex, converted, result); - Test(latex.Replace(",", ";"), converted, result); - } - [Theory] - [InlineData(@"\>\:\pi", @"\mathrm{\pi }", @"\mathrm{\pi }")] - [InlineData(@"3\quad 2", @"3\cdot 2", "6")] - [InlineData(@"a\textstyle b\displaystyle", @"ab", @"ab")] - [InlineData(@"a^6+2a^6 % should be 3a^6", @"a^6+2a^6", @"3a^6")] - [InlineData(@"4+\ \mkern1.5mu3", @"4+3", "7")] - public void SkipInvisible(string latex, string converted, string output) => Test(latex, converted, output); - [Theory] - [InlineData(@"", "There is nothing to evaluate")] - [InlineData(@"\ ", "There is nothing to evaluate")] - [InlineData(@"\;", "There is nothing to evaluate")] - [InlineData(@"\quad", "There is nothing to evaluate")] - [InlineData(@"+", "Missing right operand for +")] - [InlineData(@"-", "Missing right operand for \u2212")] - [InlineData(@"\times", "Unsupported Unary Operator ×")] - [InlineData(@"\div", "Unsupported Unary Operator ÷")] - [InlineData(@"\%", "Missing left operand for %")] - [InlineData(@"\degree", "Missing left operand for °")] - [InlineData(@"\dagger", "Unsupported Unary Operator †")] - [InlineData(@"\times x", "Unsupported Unary Operator ×")] - [InlineData(@"\div x", "Unsupported Unary Operator ÷")] - [InlineData(@"\% x", "Missing left operand for %")] - [InlineData(@"\degree x", "Missing left operand for °")] - [InlineData(@"x+", "Missing right operand for +")] - [InlineData(@"x-", "Missing right operand for \u2212")] - [InlineData(@"x\times", "Missing right operand for ×")] - [InlineData(@"x\div", "Missing right operand for ÷")] - [InlineData(@"x\dagger", "Unsupported Binary Operator †")] - [InlineData(@"+^2", "Superscripts are unsupported for Unary Operator +")] - [InlineData(@"+_2", "Missing right operand for +")] - [InlineData(@"+^21", "Superscripts are unsupported for Unary Operator +")] - [InlineData(@"+_21", "Subscripts are unsupported for Unary Operator +")] - [InlineData(@"1+^21", "Superscripts are unsupported for Binary Operator +")] - [InlineData(@"1+_21", "Subscripts are unsupported for Binary Operator +")] - [InlineData(@"-_31", "Subscripts are unsupported for Unary Operator −")] - [InlineData(@"1\times_41", "Subscripts are unsupported for Binary Operator ×")] - [InlineData(@"\div_51", "Unsupported Unary Operator ÷")] - [InlineData(@"1\%_6", "Subscripts are unsupported for Ordinary %")] - [InlineData(@"1\degree_7", "Subscripts are unsupported for Ordinary °")] - [InlineData(@"\dagger_8", "Unsupported Unary Operator †")] - [InlineData(@".", "Invalid number: .")] - [InlineData(@"..", "Invalid number: ..")] - [InlineData(@"1..", "Invalid number: 1..")] - [InlineData(@"..1", "Invalid number: ..1")] - [InlineData(@"a_+", "Unsupported Unary Operator + in subscript")] - [InlineData(@"a_|", "Unsupported Ordinary | in subscript")] - [InlineData(@"a_{1+1}", "Unsupported Binary Operator + in subscript")] - [InlineData(@"a_{2^3}", "Unsupported exponentiation in subscript")] - [InlineData(@"a_{a2^3}", "Unsupported exponentiation in subscript")] - [InlineData(@"a_{a^32}", "Unsupported exponentiation in subscript")] - [InlineData(@"a_{2_3}", "Unsupported subscript in subscript")] - [InlineData(@"a_{a2_3}", "Unsupported subscript in subscript")] - [InlineData(@"a_{a_32}", "Unsupported subscript in subscript")] - [InlineData(@"a_{1,2}", "Unsupported Punctuation , in subscript")] - [InlineData(@"\square", "Placeholders should be filled")] - [InlineData(@"x^\square", "Placeholders should be filled")] - [InlineData(@"\square^x", "Placeholders should be filled")] - [InlineData(@"a_\square", "Placeholders should be filled")] - [InlineData(@"\square_a", "Placeholders should be filled")] - [InlineData(@"(", "Missing )")] - [InlineData(@"(_21)", "Subscripts are unsupported for Open (")] - [InlineData(@"(x", "Missing )")] - [InlineData(@"((x)", "Missing )")] - [InlineData(@"(+", "Missing right operand for +")] - [InlineData(@")", "Missing (")] - [InlineData(@"(1)_2", "Subscripts are unsupported for Close )")] - [InlineData(@"x)", "Missing (")] - [InlineData(@"(x))", "Missing (")] - [InlineData(@"+)", "Missing right operand for +")] - [InlineData(@"\left(\right)", "Missing math inside ( )")] - [InlineData(@"\left(1+\right)", "Missing right operand for +")] - [InlineData(@"\left(2,3\right)^\square", "Placeholders should be filled")] - [InlineData(@"(2,3)^\square", "Placeholders should be filled")] - [InlineData(@"[", "Missing ]")] - [InlineData(@"[_21)", "Unrecognized bracket pair [ )")] - [InlineData(@"[x", "Missing ]")] - [InlineData(@"[x)", "Unrecognized bracket pair [ )")] - [InlineData(@"[[x)", "Unrecognized bracket pair [ )")] - [InlineData(@"[+", "Missing right operand for +")] - [InlineData(@"[x))", "Unrecognized bracket pair [ )")] - [InlineData(@"\left[\right)", "Unrecognized bracket pair [ )")] - [InlineData(@"\left[1+\right)", "Missing right operand for +")] - [InlineData(@"\left[2,3\right)^\square", "Placeholders should be filled")] - [InlineData(@"[2,3)^\square", "Placeholders should be filled")] - [InlineData(@"((x]", "Unrecognized bracket pair ( ]")] - [InlineData(@"(x]", "Unrecognized bracket pair ( ]")] - [InlineData(@"]", "Missing [")] - [InlineData(@"]_2", "Subscripts are unsupported for Close ]")] - [InlineData(@"x]", "Missing [")] - [InlineData(@"(x]]", "Unrecognized bracket pair ( ]")] - [InlineData(@"+]", "Missing right operand for +")] - [InlineData(@"\left(\right]", "Unrecognized bracket pair ( ]")] - [InlineData(@"\left(1+\right]", "Missing right operand for +")] - [InlineData(@"\left(2,3\right]^\square", "Placeholders should be filled")] - [InlineData(@"(2,3]^\square", "Placeholders should be filled")] - [InlineData(@"[]", "Missing math inside [ ]")] - [InlineData(@"[[x]", "Missing ]")] - [InlineData(@"[x]]", "Missing [")] - [InlineData(@"[_\square x]", "Subscripts are unsupported for Open [")] - [InlineData(@"[x]_\square", "Subscripts are unsupported for Close ]")] - [InlineData(@"\left[\right]", "Missing math inside [ ]")] - [InlineData(@"\left[1+\right]", "Missing right operand for +")] - [InlineData(@"\left[2,3\right]^\square", "Placeholders should be filled")] - [InlineData(@"[2,3]^\square", "Placeholders should be filled")] - [InlineData(@"\{", "Missing }")] - [InlineData(@"\{_2\}", "Subscripts are unsupported for Open {")] - [InlineData(@"\{x", "Missing }")] - [InlineData(@"\{\{x\}", "Missing }")] - [InlineData(@"\{+", "Missing right operand for +")] - [InlineData(@"\}", "Missing {")] - [InlineData(@"\}_2", "Subscripts are unsupported for Close }")] - [InlineData(@"x\}", "Missing {")] - [InlineData(@"\{x\}\}", "Missing {")] - [InlineData(@"+\}", "Missing right operand for +")] - [InlineData(@"\left\{1+\right\}", "Missing right operand for +")] - [InlineData(@"\left\{\{\right\}\}", @"Missing }")] - [InlineData(@"\{2,3\}^\square", "Placeholders should be filled")] - [InlineData(@"(^2)", "Superscripts are unsupported for Open Bracket (")] - [InlineData(@"[^2]", "Superscripts are unsupported for Open Bracket [")] - [InlineData(@"\{^2\}", "Superscripts are unsupported for Open Bracket {")] - [InlineData(@"\left\{2,3\right\}^\square", "Placeholders should be filled")] - [InlineData(@"\frac{}{x}", "Missing numerator")] - [InlineData(@"\frac{x}{}", "Missing denominator")] - [InlineData(@"\sqrt{}", "Missing radicand")] - [InlineData(@"\sin", "Missing argument for sin")] - [InlineData(@"\cos-", "Missing right operand for \u2212")] - [InlineData(@"\tan\times", "Unsupported Unary Operator ×")] - [InlineData(@"\cot^(-1)", "Missing )")] - [InlineData(@"\sec\csc", "Missing argument for csc")] - [InlineData(@"\arcsin_2x", "Subscripts are unsupported for Large Operator arcsin")] - [InlineData(@"\operatorname{dab}", "Unsupported Large Operator dab")] - [InlineData(@"\begin{matrix}\end{matrix}", "Missing matrix element")] - [InlineData(@"\begin{matrix}2&3\end{matrix}_2", "Subscripts are unsupported for Ordinary")] - [InlineData(@"\begin{matrix}2&3\end{matrix}^2", "Superscripts are unsupported for Ordinary")] - [InlineData(@"\begin{pmatrix}2&+\end{pmatrix}", "Missing right operand for +")] - [InlineData(@"\begin{pmatrix}2&3\end{pmatrix}_2", "Subscripts are unsupported for Inner ")] - [InlineData(@"\begin{Vmatrix}\end{Vmatrix}", "Missing matrix element")] - [InlineData(@"\begin{Vmatrix}2&3\end{Vmatrix}", "Unrecognized bracket pair ‖ ‖")] - [InlineData(@"\begin{eqnarray}&&\end{eqnarray}", "Unsupported table environment eqnarray")] - [InlineData(@",", "Missing left operand for comma")] - [InlineData(@"1,_22", "Subscripts are unsupported for Punctuation ,")] - [InlineData(@"1,", "Missing right operand for comma")] - [InlineData(@",1", "Missing left operand for comma")] - [InlineData(@",1,2", "Missing left operand for comma")] - [InlineData(@"1,,2", "Missing left operand for comma")] - [InlineData(@"1,2,", "Missing right operand for comma")] - [InlineData(@",,1,2", "Missing left operand for comma")] - [InlineData(@"1,,2,", "Missing left operand for comma")] - [InlineData(@"1,2,,", "Missing left operand for comma")] - [InlineData(@"\arcsin(1,2)", "Comma cannot be argument for arcsin")] - [InlineData(@"[5,6)\times", "Missing right operand for ×")] - [InlineData(@"\sqrt[{[)}]{}", "Unrecognized bracket pair [ )")] - [InlineData(@"\sqrt[{[a,b]}]{}", "Missing radicand")] - [InlineData(@"\cap", "Unsupported Unary Operator ∩")] - [InlineData(@"\cap1", "Unsupported Unary Operator ∩")] - [InlineData(@"1\cap", "Missing right operand for ∩")] - [InlineData(@"\cup", "Unsupported Unary Operator ∪")] - [InlineData(@"\cup1", "Unsupported Unary Operator ∪")] - [InlineData(@"1\cup", "Missing right operand for ∪")] - [InlineData(@"\setminus", "Unsupported Unary Operator ∖")] - [InlineData(@"\setminus1", "Unsupported Unary Operator ∖")] - [InlineData(@"1\setminus", "Missing right operand for ∖")] - [InlineData(@"^\complement", "Superscripts are unsupported for Ordinary")] - [InlineData(@"_\complement", "Subscripts are unsupported for Ordinary")] - [InlineData(@"1_\complement", "Subscripts are unsupported for Number 1")] - [InlineData(@"x_\complement", "Unsupported Ordinary ∁ in subscript")] - [InlineData(@"\sin_\complement x", "Subscripts are unsupported for Large Operator sin")] - [InlineData(@",\cap", "Missing left operand for comma")] - [InlineData(@";\cap", "Missing left operand for comma")] - [InlineData(@"1,\cap", "Unsupported Unary Operator ∩")] - [InlineData(@"1;\cap", "Unsupported Unary Operator ∩")] - [InlineData(@"\cap,", "Unsupported Unary Operator ∩")] - [InlineData(@"\cap;", "Unsupported Unary Operator ∩")] - [InlineData(@"\left|1,2\right|", "Comma cannot be abs argument")] - [InlineData(@"1^+", "+ alone in superscript but not in limit subscript context")] - [InlineData(@"1^-", "\u2212 alone in superscript but not in limit subscript context")] - [InlineData(@"\lim", "Missing limit variable in subscript")] - [InlineData(@"\lim_x", "Missing → in limit subscript")] - [InlineData(@"\lim_{xy}", "Missing → in limit subscript")] - [InlineData(@"\lim_{xy^+}", "Missing → in limit subscript")] - [InlineData(@"\lim_{xy^-}", "Missing → in limit subscript")] - [InlineData(@"\lim_{\to}", "Missing limit variable in subscript")] - [InlineData(@"\lim_{\to2}", "Missing limit variable in subscript")] - [InlineData(@"\lim_{x\to}", "Missing limit target in subscript")] - [InlineData(@"\lim_{xy\to}", "Missing limit target in subscript")] - [InlineData(@"\lim_{xy\to^+}", "Missing limit target in subscript")] - [InlineData(@"\lim_{xy\to^-}", "Missing limit target in subscript")] - [InlineData(@"\lim_{x\to2^+2}", "Limit direction indicator + not placed at the end")] - [InlineData(@"\lim_{xy\to2^+2}", "Limit direction indicator + not placed at the end")] - [InlineData(@"\lim_{xy\to2^-2}", "Limit direction indicator \u2212 not placed at the end")] - [InlineData(@"\lim_{xy\to(2^+)}", "+ alone in superscript but not in limit subscript context")] - [InlineData(@"\lim_{xy\to(2^-)}", "\u2212 alone in superscript but not in limit subscript context")] - [InlineData(@"\lim_{xy\to\left(2^+\right)}", "+ alone in superscript but not in limit subscript context")] - [InlineData(@"\lim_{xy\to\left(2^-\right)}", "\u2212 alone in superscript but not in limit subscript context")] - [InlineData(@"\lim_{x,y}", "Comma cannot be limit variable in subscript")] - [InlineData(@"\lim_{x\to2}", "Missing right operand for lim")] - [InlineData(@"(\lim_{x\to2})x", "Missing right operand for lim")] - [InlineData(@"\lim_{x\to2}\cdot 1", "Unsupported Unary Operator ·")] - [InlineData(@"\lim_{x\to2}^6x", "Superscripts are unsupported for Large Operator lim")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}}", "Missing derivative variable")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}", "Missing right operand for derivative operator")] - [InlineData(@"\frac{\mathrm{d}^{1.5}}{\mathrm{d}x^{1.5}}", "Derivative order must be an integer, got 1.5")] - [InlineData(@"\frac{\mathrm{d}^a}{\mathrm{d}x^a}", "Derivative order must be an integer")] - [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}}", "Missing derivative variable")] - [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}^2}", "The d in derivative denominator cannot have an exponent. Did you mean to write it at the end of the denominator?")] - [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}^2}", "The d in derivative denominator cannot have an exponent. Did you mean to write it at the end of the denominator?")] - [InlineData(@"\frac{\mathrm{d}^3}{\mathrm{d}x^2}", "Derivative order mismatch: 3 in numerator but 2 is in denominator")] - [InlineData(@"\frac{\mathrm{d}^0}{\mathrm{d}x^1}", "Derivative order mismatch: 0 in numerator but 1 is in denominator")] - [InlineData(@"\frac{\mathrm{d}^{-1}}{\mathrm{d}x^1}", "Derivative order mismatch: -1 in numerator but 1 is in denominator")] - [InlineData(@"\frac{\mathrm{d}^{-3}}{\mathrm{d}x^{-2}}", "Derivative order mismatch: -3 in numerator but -2 is in denominator")] - [InlineData(@"\frac{\mathrm{d}^{-3}}{\mathrm{d}x^{-2.5}}", "Derivative order must be an integer, got -2.5")] - [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}(x^2)}", "Derivative order mismatch: 2 in numerator requires 2 in denominator")] - [InlineData(@"\frac{\mathrm{d}^2\cdot 1}{\mathrm{d}x^2}", "Unsupported Unary Operator ·")] - [InlineData(@"\left(\frac{\mathrm{d}^2}{\mathrm{d}x^2}\right)x", "Missing right operand for derivative operator")] - [InlineData(@"\int", "Missing integral variable. Did you forget to prepend it with upright d?")] - [InlineData(@"\int_0", "Integrals with only the lower limit are not supported")] - [InlineData(@"\int^0", "Integrals with only the upper limit are not supported")] - [InlineData(@"\int0", "Missing integral variable. Did you forget to prepend it with upright d?")] - [InlineData(@"\int_0^0", "Missing integral variable. Did you forget to prepend it with upright d?")] - [InlineData(@"\mathrm{d}", "d alone but not in integral body context")] - [InlineData(@"\int(\mathrm{d}", "d alone but not in integral body context")] - [InlineData(@"\int1,2\mathrm{d}x", "d alone but not in integral body context")] - [InlineData(@"\mathrm{for}", "Unsupported Unary Operator for")] - [InlineData(@"1\mathrm{for}", "Missing right operand for for")] - [InlineData(@"\mathrm{for}2", "Unsupported Unary Operator for")] - [InlineData(@"\mathrm{for},", "Unsupported Unary Operator for")] - [InlineData(@"1\mathrm{for},", "Missing right operand for for")] - [InlineData(@"1\mathrm{for}^23,", "for operator cannot have a superscript")] - [InlineData(@"1\mathrm{for}_23,", "for operator cannot have a subscript")] - [InlineData(@"\begin{cases}1,2\end{cases}", "Comma cannot be case expression")] - [InlineData(@"\begin{cases}1&3,4\end{cases}", "Comma cannot be case predicate")] - [InlineData(@"\begin{cases}1,2&3,4\end{cases}", "Comma cannot be case expression")] - [InlineData(@"\begin{cases}&3,4\end{cases}", "Missing case expression")] - [InlineData(@"\begin{cases}1&\end{cases}", "Missing case predicate")] - [InlineData(@"\begin{cases}1&\mathrm{for}^23\end{cases}", "for operator cannot have a superscript")] - [InlineData(@"\begin{cases}1&\mathrm{for}_23\end{cases}", "for operator cannot have a subscript")] - [InlineData(@"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}&ERROR\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", "The cases environment must have 1 to 2 columns per row")] - public void Error(string badLaTeX, string error) => - Evaluation.Evaluate(ParseLaTeX(badLaTeX)) - .Match(entity => throw new Xunit.Sdk.XunitException(entity.Latexise()), e => Assert.Equal(error, e)); - // https://github.com/ForNeVeR/wpf-math/issues/42 - [Theory] - [InlineData("1 + 1 - 1", @"1+1-1")] - [InlineData("3 / 2", @"\frac{3}{2}")] - [InlineData("2 * 3 / 5", @"\frac{2\cdot 3}{5}")] - [InlineData("sin(x+pi)", @"\sin \left( x+\mathrm{\pi }\right) ")] - [InlineData("sin(aaa)", @"\sin \left( \mathrm{aaa}\right) ")] - [InlineData("sin(aaa aaa aa)", @"\sin \left( \mathrm{aaa}\cdot \mathrm{aaa}\cdot \mathrm{aa}\right) ")] - [InlineData("log(3,alpha)", @"\log _3\left( \alpha \right) ")] - [InlineData("a_2k", @"a_{\mathrm{2k}}")] - [InlineData("2e9", @"2000000000")] - [InlineData("-3e4^x", @"-30000^x")] - [InlineData("(-3e4)^x", @"\left( -30000\right) ^x")] - [InlineData("i+2i", @"\mathrm{i}+2\mathrm{i}")] - public void SimpleArithmeticSyntax(string simpleSyntax, string latex) => - Assert.Equal(latex, LaTeXParser.MathListToLaTeX(Evaluation.Visualize((Entity)simpleSyntax)).ToString()); - [Theory] - [InlineData(@"\top", @"\top ", @"\top ")] - [InlineData(@"\bot", @"\bot ", @"\bot ")] - [InlineData(@"\neg\top", @"\neg \top ", @"\bot ")] - [InlineData(@"\neg\bot", @"\neg \bot ", @"\top ")] - [InlineData(@"\top\land\top", @"\top \wedge \top ", @"\top ")] - [InlineData(@"\top\land\bot", @"\top \wedge \bot ", @"\bot ")] - [InlineData(@"\bot\land\top", @"\bot \wedge \top ", @"\bot ")] - [InlineData(@"\bot\land\bot", @"\bot \wedge \bot ", @"\bot ")] - [InlineData(@"\top\lor\top", @"\top \vee \top ", @"\top ")] - [InlineData(@"\top\lor\bot", @"\top \vee \bot ", @"\top ")] - [InlineData(@"\bot\lor\top", @"\bot \vee \top ", @"\top ")] - [InlineData(@"\bot\lor\bot", @"\bot \vee \bot ", @"\bot ")] - [InlineData(@"\top\veebar\top", @"\top \veebar \top ", @"\bot ")] - [InlineData(@"\top\veebar\bot", @"\top \veebar \bot ", @"\top ")] - [InlineData(@"\bot\veebar\top", @"\bot \veebar \top ", @"\top ")] - [InlineData(@"\bot\veebar\bot", @"\bot \veebar \bot ", @"\bot ")] - [InlineData(@"\top\barwedge\top", @"\neg \left( \top \wedge \top \right) ", @"\bot ")] - [InlineData(@"\top\barwedge\bot", @"\neg \left( \top \wedge \bot \right) ", @"\top ")] - [InlineData(@"\bot\barwedge\top", @"\neg \left( \bot \wedge \top \right) ", @"\top ")] - [InlineData(@"\bot\barwedge\bot", @"\neg \left( \bot \wedge \bot \right) ", @"\top ")] - [InlineData(@"\top\rightarrow\top", @"\top \rightarrow \top ", @"\top ")] - [InlineData(@"\top\rightarrow\bot", @"\top \rightarrow \bot ", @"\bot ")] - [InlineData(@"\bot\rightarrow\top", @"\bot \rightarrow \top ", @"\top ")] - [InlineData(@"\bot\rightarrow\bot", @"\bot \rightarrow \bot ", @"\top ")] - [InlineData(@"\top\nrightarrow\top", @"\neg \left( \top \rightarrow \top \right) ", @"\bot ")] - [InlineData(@"\top\nrightarrow\bot", @"\neg \left( \top \rightarrow \bot \right) ", @"\top ")] - [InlineData(@"\bot\nrightarrow\top", @"\neg \left( \bot \rightarrow \top \right) ", @"\bot ")] - [InlineData(@"\bot\nrightarrow\bot", @"\neg \left( \bot \rightarrow \bot \right) ", @"\bot ")] - [InlineData(@"\top\leftarrow\top", @"\top \rightarrow \top ", @"\top ")] - [InlineData(@"\bot\leftarrow\top", @"\top \rightarrow \bot ", @"\bot ")] - [InlineData(@"\top\leftarrow\bot", @"\bot \rightarrow \top ", @"\top ")] - [InlineData(@"\bot\leftarrow\bot", @"\bot \rightarrow \bot ", @"\top ")] - [InlineData(@"\top\nleftarrow\top", @"\neg \left( \top \rightarrow \top \right) ", @"\bot ")] - [InlineData(@"\bot\nleftarrow\top", @"\neg \left( \top \rightarrow \bot \right) ", @"\top ")] - [InlineData(@"\top\nleftarrow\bot", @"\neg \left( \bot \rightarrow \top \right) ", @"\bot ")] - [InlineData(@"\bot\nleftarrow\bot", @"\neg \left( \bot \rightarrow \bot \right) ", @"\bot ")] - [InlineData(@"\top\leftrightarrow\top", @"\neg \left( \top \veebar \top \right) ", @"\top ")] - [InlineData(@"\bot\leftrightarrow\top", @"\neg \left( \bot \veebar \top \right) ", @"\bot ")] - [InlineData(@"\top\leftrightarrow\bot", @"\neg \left( \top \veebar \bot \right) ", @"\bot ")] - [InlineData(@"\bot\leftrightarrow\bot", @"\neg \left( \bot \veebar \bot \right) ", @"\top ")] - [InlineData(@"\top\nleftrightarrow\top", @"\top \veebar \top ", @"\bot ")] - [InlineData(@"\bot\nleftrightarrow\top", @"\bot \veebar \top ", @"\top ")] - [InlineData(@"\top\nleftrightarrow\bot", @"\top \veebar \bot ", @"\top ")] - [InlineData(@"\bot\nleftrightarrow\bot", @"\bot \veebar \bot ", @"\bot ")] - [InlineData(@"x=x", @"x=x", @"\top ")] - [InlineData(@"x\le x", @"x\leq x", @"\top ")] - [InlineData(@"x\leq x", @"x\leq x", @"\top ")] - [InlineData(@"x\leqslant x", @"x\leq x", @"\top ")] - [InlineData(@"x\neq y", @"x\neq y", @"x\neq y")] // Cannot simplify without knowing x and y - [InlineData(@"1<2", @"1<2", @"\top ")] - [InlineData(@"2<1", @"2<1", @"\bot ")] - [InlineData(@"1\le1", @"1\leq 1", @"\top ")] - [InlineData(@"2>1", @"2>1", @"\top ")] - [InlineData(@"1>2", @"1>2", @"\bot ")] - [InlineData(@"1\ge1", @"1\geq 1", @"\top ")] - [InlineData(@"1\geq1", @"1\geq 1", @"\top ")] - [InlineData(@"1\geqslant1", @"1\geq 1", @"\top ")] - [InlineData(@"x\in\{x,y\}", @"x\in \left\{ x,y\right\} ", @"\top ")] - [InlineData(@"z\notin\{x,y\}", @"\neg z\in \left\{ x,y\right\} ", @"\neg z\in \left\{ x,y\right\} ")] // Cannot determine this without knowing x, y, and z - [InlineData(@"\{x,y\}\ni x", @"x\in \left\{ x,y\right\} ", @"\top ")] - public void LogicalAndRelationalOperators(string latex, string converted, string result) => Test(latex, converted, result); - - [Theory] - // Test that ∧ binds tighter than ∨ - [InlineData(@"\bot\land\bot\lor\top", @"\bot \wedge \bot \vee \top ", @"\top ")] - [InlineData(@"\top\lor\bot\land\bot", @"\top \vee \bot \wedge \bot ", @"\top ")] - // Test that ∧ binds tighter than ⊻ - [InlineData(@"\top\veebar\bot\land\top", @"\top \veebar \bot \wedge \top ", @"\top ")] - [InlineData(@"\top\land\bot\veebar\top", @"\top \wedge \bot \veebar \top ", @"\top ")] - // Test that ⊻ binds tighter than ∨ - [InlineData(@"\top\veebar \bot\lor \top", @"\top \veebar \bot \vee \top ", @"\top ")] - [InlineData(@"\top\vee \bot\veebar \top", @"\top \vee \bot \veebar \top ", @"\top ")] - // Test that → is right associative and lowest precedence - [InlineData(@"a\to b\lor c", @"a\rightarrow b\vee c", @"a\rightarrow b\vee c")] - [InlineData(@"a\lor b\to c", @"a\vee b\rightarrow c", @"a\vee b\rightarrow c")] - [InlineData(@"a\to b\to c", @"a\rightarrow b\rightarrow c", @"a\rightarrow b\rightarrow c")] - [InlineData(@"a\to (b\to c)", @"a\rightarrow b\rightarrow c", @"a\rightarrow b\rightarrow c")] - [InlineData(@"(a\to b)\to c", @"\left( a\rightarrow b\right) \rightarrow c", @"\left( a\rightarrow b\right) \rightarrow c")] - // Complex expression - [InlineData(@"a\to b\land c\lor d", @"a\rightarrow b\wedge c\vee d", @"a\rightarrow b\wedge c\vee d")] - // Test ¬ has highest precedence - [InlineData(@"\neg \top\land\top", @"\neg \top \wedge \top ", @"\bot ")] - [InlineData(@"\neg a\lor\neg a", @"\neg a\vee \neg a", @"\neg a")] - // Test relational operators bind tighter than logical - [InlineData(@"a=b\land c=d", @"a=b\wedge c=d", @"a=b\wedge c=d")] - [InlineData(@"ad", @"ad", @"ad")] - public void LogicalOperatorPrecedence(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"13")] - [InlineData(@"1<(2<3)", @"1<\left( 2<3\right) ", @"1<\top ", @"\bot ", @"1>\bot ")] - [InlineData(@"1<2<3", @"1<2<3", @"\top ", @"\bot ", @"\bot ")] - [InlineData(@"3<2<3", @"3<2<3", @"\bot ")] - [InlineData(@"1<2<1", @"1<2<1", @"\bot ")] - [InlineData(@"1<1<1", @"1<1<1", @"\bot ", @"\top ")] - [InlineData(@"1\leq 2<3", @"1\leq 2<3", @"\top ", @"\bot ", @"\bot ")] - [InlineData(@"3\leq 2<3", @"3\leq 2<3", @"\bot ")] - [InlineData(@"1\leq 2<1", @"1\leq 2<1", @"\bot ")] - [InlineData(@"1\leq 1<1", @"1\leq 1<1", @"\bot ", @"\top ", @"\bot ")] - [InlineData(@"1\leq 1=1", @"1\leq 1=1", @"\top ")] - [InlineData(@"1\leq 1\geq1", @"1\leq 1\geq 1", @"\top ")] - public void ChainedComparisons(string latex, string converted, string result, string? eqResult = null, string? geqResult = null) { - Test(latex, converted, result); - eqResult ??= result.Replace("<", "=").Replace(@"\leq ", "="); - Test(latex.Replace("<", "=").Replace(@"\leq ", "="), - converted.Replace("<", "=").Replace(@"\leq ", "="), - eqResult); - Test(latex.Replace("<", ">").Replace(@"\leq ", @"\geq "), - converted.Replace("<", ">").Replace(@"\leq ", @"\geq "), - geqResult ?? result.Replace("<", ">").Replace(@"\leq ", @"\geq ")); - Test(latex.Replace("<", @"\neq ").Replace(@"\leq ", @"\neq "), - converted.Replace("<", @"\neq ").Replace(@"\leq ", @"\neq "), - eqResult.Replace("=", @"\neq ").Replace(@"\top ", @"|||").Replace(@"\bot ", @"\top ").Replace(@"|||", @"\bot ")); - } - [Theory] - [InlineData(@"\mathrm{undefined}", @"\mathrm{undefined}", @"\mathrm{undefined}")] - [InlineData(@"0\times\infty", @"0\cdot \infty ", @"\mathrm{undefined}")] - [InlineData(@"\mathrm{undefined}+1", @"\mathrm{undefined}+1", @"\mathrm{undefined}")] - [InlineData(@"\sin\mathrm{undefined}", @"\sin \left( \mathrm{undefined}\right) ", @"\mathrm{undefined}")] - public void Undefined(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\left|1\right|", @"\left| 1\right| ", @"1")] - [InlineData(@"-\left|-1\right|", @"-\left| -1\right| ", @"-1")] - [InlineData(@"-\operatorname{abs}(-1)", @"-\left| -1\right| ", @"-1")] - [InlineData(@"-\operatorname{abs}\left|-1\right|", @"-\left| \left| -1\right| \right| ", @"-1")] - [InlineData(@"-\left|1\right|^2", @"-\left| 1\right| ^2", @"-1")] - [InlineData(@"\operatorname{sgn}\operatorname{abs} x", @"\operatorname{sgn} \left( \left| x\right| \right) ", @"1")] - public void Abs(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\lim_{x\to2}x+1", @"\lim _{x\rightarrow 2}x+1", @"3")] - [InlineData(@"\lim_{x\to2^-}x+1", @"\lim _{x\rightarrow 2^-}x+1", @"3")] - [InlineData(@"\lim_{x\to2^+}x+1", @"\lim _{x\rightarrow 2^+}x+1", @"3")] - [InlineData(@"\lim_{x\to\infty}x", @"\lim _{x\rightarrow \infty }x", @"\infty ")] - [InlineData(@"\lim_{x\to0}\frac1x+1", @"\lim _{x\rightarrow 0}\frac{1}{x}+1", @"\mathrm{undefined}")] - [InlineData(@"\lim_{x\to0^-}\frac1x+1", @"\lim _{x\rightarrow 0^-}\frac{1}{x}+1", @"-\infty ")] - [InlineData(@"\lim_{x\to0^+}\frac1x+1", @"\lim _{x\rightarrow 0^+}\frac{1}{x}+1", @"\infty ")] - [InlineData(@"\lim_{x\to\infty}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty }\left( \frac{1}{x}\right) ^2x^2+1", @"2")] - [InlineData(@"\lim_{x\to\infty^-}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty ^-}\left( \frac{1}{x}\right) ^2x^2+1", @"2")] - [InlineData(@"\lim_{x\to\infty^+}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty ^+}\left( \frac{1}{x}\right) ^2x^2+1", @"2")] - [InlineData(@"\lim_{x\to1+x}x", @"\lim _{x\rightarrow 1+x}x", @"\infty ")] - [InlineData(@"\lim_{x\to(1+x)}x", @"\lim _{x\rightarrow 1+x}x", @"\infty ")] - [InlineData(@"\lim_{x\to(1+x)^+}x", @"\lim _{x\rightarrow \left( 1+x\right) ^+}x", @"\infty ")] - [InlineData(@"\lim_{x\to(1+x)^-}x", @"\lim _{x\rightarrow \left( 1+x\right) ^-}x", @"\infty ")] - [InlineData(@"\lim_{x\to(y)}(x)y+y", @"\lim _{x\rightarrow y}xy+y", @"y^2+y")] - [InlineData(@"\lim_{x\to(y)^+}(x)y+y", @"\lim _{x\rightarrow y^+}xy+y", @"y^2+y")] - [InlineData(@"\lim_{x\to(y)^-}(x)y+y", @"\lim _{x\rightarrow y^-}xy+y", @"y^2+y")] - [InlineData(@"\lim_{y\to zzz_2}x", @"\lim _{y\rightarrow zzz_2}x", @"x")] - [InlineData(@"\lim_{y\to zzz_2^-}x", @"\lim _{y\rightarrow \left( zzz_2\right) ^-}x", @"x")] - [InlineData(@"\lim_{y\to zzz_2^+}x", @"\lim _{y\rightarrow \left( zzz_2\right) ^+}x", @"x")] - [InlineData(@"\lim_{xyy\to zzz_2}x", @"\lim _{xyy\rightarrow zzz_2}x", @"\lim _{xy^2\rightarrow z^2\cdot z_2}x")] - [InlineData(@"\lim_{xyy\to zzz_2^-}x", @"\lim _{xyy\rightarrow \left( zzz_2\right) ^-}x", @"\lim _{xy^2\rightarrow \left( z^2\cdot z_2\right) ^-}x")] - [InlineData(@"\lim_{xyy\to zzz_2^+}x", @"\lim _{xyy\rightarrow \left( zzz_2\right) ^+}x", @"\lim _{xy^2\rightarrow \left( z^2\cdot z_2\right) ^+}x")] - [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz}}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz}}x", @"x")] - [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz}^-}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz^{\mathnormal{-}}}}x", @"x")] - [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz^+}}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz^{\mathnormal{+}}}}x", @"x")] - [InlineData(@"(\lim_{x\to y}(z)^2)^3", @"\left( \lim _{x\rightarrow y}z^2\right) ^3", @"z^6")] - public void Limit(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}x", @"\frac{\mathrm{d}}{\mathrm{d}x}x", @"1")] - [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x}", @"\frac{\mathrm{d}}{\mathrm{d}x}x", @"1")] - [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x}x", @"\left( \frac{\mathrm{d}}{\mathrm{d}x}x\right) \cdot x", @"x")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"1+y")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}(x)}(x)y+1", @"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"1+y")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x^2}x^2y+1", @"\frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x^2\cdot y+1", @"1+y")] - [InlineData(@"\frac{\mathrm{d}x^2}{\mathrm{d}x^2}y+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x^2\right) \cdot y+1", @"1+y")] - [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x^2}xy+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x\right) \cdot xy+1", @"1")] - [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}(x^2)}(x)y+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x\right) \cdot xy+1", @"1")] - [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}x^2}x^2y+1", @"\frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}x^2}x^2\cdot y+1", @"1+2y")] - [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}(x^2)^2}x^2y+1", @"\frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}\left( x^2\right) ^2}x^2\cdot y+1", @"1")] - [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}x^2}x^2y+1", @"\left( \frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}x^2}x\right) \cdot x^2\cdot y+1", @"1")] - [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}(x^2)^2}x^2y+1", @"\left( \frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}\left( x^2\right) ^2}x\right) \cdot x^2\cdot y+1", @"1")] - [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}\lim_{y\to z}xy+x", @"\frac{\mathrm{d}}{\mathrm{d}x}\lim _{y\rightarrow z}xy+x", @"x+z")] - [InlineData(@"\frac{\mathrm{d}^0x}{\mathrm{d}x^0}y+1", @"\left( \frac{\mathrm{d^{\mathnormal{0}}}}{\mathrm{d}x^0}x\right) \cdot y+1", @"1+xy")] - [InlineData(@"\frac{\mathrm{d}^{-1}x}{\mathrm{d}x^{-1}}y+1", @"\left( \int x\, \mathrm{d}x\right) \cdot y+1", @"\left( \frac{x^2}{2}+C\right) \cdot y+1")] - [InlineData(@"\frac\mathrm{dy}\mathrm{dx}\mathrm{dx}", @"\frac{\mathrm{dy}}{\mathrm{dx}}\cdot \mathrm{dx}", @"\mathrm{dy}\quad \mathrm{for}\quad \mathrm{dx}\neq 0")] // upright letters are interpreted together. - [InlineData(@"\frac\mathrm{d\ y}\mathrm{d\ x}", @"\frac{\mathrm{d}}{\mathrm{d}x}y", @"0")] // spaces are required to separate upright letters. - [InlineData(@"\frac\mathrm{dx}\mathrm{dx}", @"\frac{\mathrm{dx}}{\mathrm{dx}}", @"1\quad \mathrm{for}\quad \mathrm{dx}\neq 0")] - public void Derivative(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\int\mathrm{d}x", @"\int 1\, \mathrm{d}x", @"C+x")] - [InlineData(@"\int\int\mathrm{d}x\mathrm{d}y", @"\int \int 1\, \mathrm{d}x\, \mathrm{d}y", @"C_1+\left( C+x\right) \cdot y")] - [InlineData(@"(\int\int\mathrm{d}x\mathrm{d}y)", @"\int \int 1\, \mathrm{d}x\, \mathrm{d}y", @"C_1+\left( C+x\right) \cdot y")] - [InlineData(@"\int\int C\mathrm{d}x\mathrm{d}y", @"\int \int C\, \mathrm{d}x\, \mathrm{d}y", @"\left( C_1+Cx\right) \cdot y+C_2")] - [InlineData(@"\int\frac\mathrm{d}{\mathrm{d}x}\int C\mathrm{d}x\mathrm{d}y", @"\int \frac{\mathrm{d}}{\mathrm{d}x}\int C\, \mathrm{d}x\, \mathrm{d}y", @"C_1+Cy")] - [InlineData(@"\int x\,\mathrm{d}x", @"\int x\, \mathrm{d}x", @"\frac{x^2}{2}+C")] - [InlineData(@"\int x+y\,\mathrm{d}x", @"\int \left( x+y\right) \, \mathrm{d}x", @"\frac{\left( x+y\right) ^2}{2}+C")] - [InlineData(@"2\int x+y\,\mathrm{d}x\cdot 3", @"2\left( \int \left( x+y\right) \, \mathrm{d}x\right) \cdot 3", @"6\left( C+\frac{\left( x+y\right) ^2}{2}\right) ")] - [InlineData(@"2+\int\sin x+y\,\mathrm{d}\sin x+3", @"2+\int \left( \sin \left( x\right) +y\right) \, \mathrm{d}\left( \sin \left( x\right) \right) +3", @"5+\frac{\left( \sin \left( x\right) +y\right) ^2}{2}+C")] - [InlineData(@"2+\int xy+z\,\mathrm{d}xy\cdot 3", @"2+\left( \int \left( xy+z\right) \, \mathrm{d}\left( xy\right) \right) \cdot 3", @"2+\left( \frac{\left( xy+z\right) ^2}{2}+C\right) \cdot 3")] - [InlineData(@"\int_0^1\mathrm{d}x", @"\int _0^11\, \mathrm{d}x", @"1")] - [InlineData(@"\int_{10}^{11}\mathrm{d}x", @"\int _{10}^{11}1\, \mathrm{d}x", @"1")] - [InlineData(@"\int_{10}^{11}x\mathrm{d}x", @"\int _{10}^{11}x\, \mathrm{d}x", @"\frac{21}{2}")] - [InlineData(@"\int_{-\infty}^\infty\frac1{x^2+1}\mathrm{d}x", @"\int _{-\infty }^{\infty }\frac{1}{x^2+1}\, \mathrm{d}x", @"\mathrm{\pi }")] - public void Integral(string latex, string converted, string result) => Test(latex, converted, result); - [Theory] - [InlineData(@"x\mathrm{for}y", @"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y")] - [InlineData(@"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y")] - [InlineData(@"(x\mathrm{for}y)\mathrm{for}z", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] - [InlineData(@"x\mathrm{for}(y\mathrm{for}z)", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] - [InlineData(@"x\mathrm{for}y\mathrm{for}z", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] - [InlineData(@"x\mathrm{for}y,y\mathrm{for}z,z\mathrm{for}a\mathrm{for}b", @"x\quad \mathrm{for}\quad y,y\quad \mathrm{for}\quad z,z\quad \mathrm{for}\quad a\quad \mathrm{for}\quad b", null)] - [InlineData(@"x=2=y\mathrm{for}x\leftrightarrow y", @"x=2=y\quad \mathrm{for}\quad \neg \left( x\veebar y\right) ", @"x=2=y\quad \mathrm{for}\quad \neg \left( x\veebar y\right) ")] - public void Provided(string latex, string converted, string? result) => Test(latex, converted, result); - [Theory] - [InlineData(@"\begin{cases}x+1\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\end{array}\right. ", - @"1+x")] - [InlineData(@"\begin{cases}x+1\\x-1\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", - @"1+x")] - [InlineData(@"\begin{cases}x+1&\mathrm{otherwise}\\x-1\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", - @"1+x")] - [InlineData(@"\begin{cases}x+1&\top\\x-1&\mathrm{otherwise}\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", - @"1+x")] - [InlineData(@"\begin{cases}x+1&\mathrm{otherwise}\\x-1&\mathrm{for}\top\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", - @"1+x")] - [InlineData(@"\begin{cases}x+1 & x<0\\x-1 & x\geq0\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ")] - [InlineData(@"\begin{cases}x+1 & \mathrm{for}\ x<0\\x-1 & \mathrm{for}\ x\geq0\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ")] - [InlineData(@"\begin{cases}x+1 & \mathrm{for} x<0\mathrm{for} y\\x-1 & \mathrm{for}\ x\geq0\mathrm{for} y\end{cases}", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }\left( x<0\quad \mathrm{for}\quad y\right) \\ \textstyle x-1&\textstyle \mathrm{for\ }\left( x\geq 0\quad \mathrm{for}\quad y\right) \end{array}\right. ", - @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }\left( x<0\quad \mathrm{for}\quad y\right) \\ \textstyle x-1&\textstyle \mathrm{for\ }\left( x\geq 0\quad \mathrm{for}\quad y\right) \end{array}\right. ")] - public void Piecewise(string latex, string converted, string result) => Test(latex, converted, result); - } +using System.Linq; +using System.Text.RegularExpressions; +using AngouriMath; +using Xunit; + +namespace CSharpMath.EvaluationTests { + using Atom; + + public class EvaluationTests { + internal static MathList ParseLaTeX(string latex) => + LaTeXParser.MathListFromLaTeX(latex).Match(list => list, e => throw new Xunit.Sdk.XunitException(e)); + static Evaluation.MathItem ParseMath(string latex) => + Evaluation.Evaluate(ParseLaTeX(latex)).Match(entity => entity, e => throw new Xunit.Sdk.XunitException(e)); + static void Test(string input, string converted, string? result) { + void Test(string input) { + var math = ParseMath(input); + Assert.NotNull(math); + Assert.Equal(converted, LaTeXParser.MathListToLaTeX(Evaluation.Visualize(math)).ToString()); + // TODO: Apparently adding this line causes a stack overflow when all tests are run, investigate later + // Assert.Equal(converted, LaTeXParser.MathListToLaTeX(Evaluation.Visualize(ParseMath(converted))).ToString()); // Double checking conversion + // Ensure that the converted entity is valid by simplifying it + if (result != null) + Assert.Equal(result, + LaTeXParser.MathListToLaTeX(Evaluation.Visualize(Assert.IsType(math).Content.Simplify())).ToString()); + else Assert.IsNotType(result); + } + Test(input); + // This regex balances (, [ and \{ with ), ] and \} into one group, then inserts \left and \right + // But does not do this for \sqrt's [ and ] + Test(Regex.Replace(input, @"(?\(|\[|\\\\{)|(?<-open>\)|\]|\\\\}))+(?(open)(?!)))(\)|\]|\\\\})", @"\left$1$2\right$3")); + } + [Theory] + [InlineData("1", "1", "1")] + [InlineData("01", "1", "1")] + [InlineData("10", "10", "10")] + [InlineData("010", "10", "10")] + [InlineData("1.", "1", "1")] + [InlineData("01.", "1", "1")] + [InlineData("1.0", "1", "1")] + [InlineData("01.0", "1", "1")] + [InlineData(".1", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData(".10", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData("1.1", @"\frac{11}{10}", @"\frac{11}{10}")] + [InlineData("01.1", @"\frac{11}{10}", @"\frac{11}{10}")] + [InlineData("1.10", @"\frac{11}{10}", @"\frac{11}{10}")] + [InlineData("01.10", @"\frac{11}{10}", @"\frac{11}{10}")] + [InlineData("0.1", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData("00.1", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData("0.10", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData("00.10", @"\frac{1}{10}", @"\frac{1}{10}")] + [InlineData("1234", "1234", "1234")] + [InlineData("0123456789", "123456789", "123456789")] + [InlineData("1234.", "1234", "1234")] + [InlineData(".5678", @"\frac{2839}{5000}", @"\frac{2839}{5000}")] + [InlineData(".9876543210", "0.9876543210", "0.9876543210")] + [InlineData("1234.5678", @"\frac{6172839}{5000}", @"\frac{6172839}{5000}")] + [InlineData(@"\infty", @"\infty ", @"\infty ")] + [InlineData(@"1_2", @"1", @"1")] + [InlineData(@"10_2", @"2", @"2")] + [InlineData(@"1._2", @"1", @"1")] + [InlineData(@"1.1_2", @"\frac{3}{2}", @"\frac{3}{2}")] + [InlineData(@".1_3", @"\frac{1}{3}", @"\frac{1}{3}")] + [InlineData(@"10_3", @"3", @"3")] + [InlineData(@"10.1_3", @"\frac{10}{3}", @"\frac{10}{3}")] + public void Numbers(string input, string converted, string output) => + Test(input, converted, output); + [Theory] + [InlineData("a", "a", "a")] + [InlineData("ab", @"ab", @"ab")] + [InlineData("abc", @"abc", @"abc")] + [InlineData("3a", @"3a", @"3a")] + [InlineData("3ab", @"3ab", @"3ab")] + [InlineData("3a3", @"3a\cdot 3", @"9a")] + [InlineData("3aa", @"3aa", @"3a^2")] + [InlineData(@"3\mathrm{aa}", @"3\mathrm{aa}", @"3\mathrm{aa}")] + [InlineData(@"3\mathrm{aa}a", @"3\mathrm{aa}a", @"3a\mathrm{aa}")] + [InlineData(@"3\mathrm{a}a", @"3aa", @"3a^2")] + [InlineData(@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + @"abcd\mathrm{e}fgh\cdot \mathrm{i}jklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + // i is considered as a number instead of a variable, unlike other alphabets, so it is sorted to the front + @"\mathrm{i}aAbBcCdD\mathrm{e}EfFgGhHIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ")] + [InlineData(@"\alpha\beta\gamma\delta\epsilon\varepsilon\zeta\eta\theta\iota\kappa\varkappa" + + @"\lambda\mu\nu\xi\omicron\pi\varpi\rho\varrho\sigma\varsigma\tau\upsilon\phi\varphi\chi" + + @"\psi\omega\Gamma\Delta\Theta\Lambda\Xi\Pi\Sigma\Upsilon\Phi\Psi\Omega", + @"\alpha \beta \gamma \delta \epsilon \varepsilon \zeta " + + @"\eta \theta \iota \kappa \varkappa \lambda \mu " + + @"\nu \xi \omicron \mathrm{\pi }\varpi \rho \varrho " + + @"\sigma \varsigma \tau \upsilon \phi \varphi \chi " + + @"\psi \omega \Gamma \Delta \Theta \Lambda \Xi " + + @"\Pi \Sigma \Upsilon \Phi \Psi \Omega ", + @"\alpha \beta \chi \delta \Delta \epsilon \eta " + + @"\gamma \Gamma \iota \kappa \lambda \Lambda \mu " + + @"\nu \omega \Omega \omicron \phi \Phi \mathrm{\pi }\Pi " + + @"\psi \Psi \rho \sigma \Sigma \tau \theta " + + @"\Theta \upsilon \Upsilon \varepsilon \varkappa \varphi " + + @"\varpi \varrho \varsigma \xi \Xi \zeta ")] + [InlineData(@"a_2", @"a_2", @"a_2")] + [InlineData(@"a_2+a_2", @"a_2+a_2", @"2a_2")] + [InlineData(@"a_{23}", @"a_{\mathrm{23}}", @"a_{\mathrm{23}}")] + [InlineData(@"\pi_a", @"\mathrm{\pi _{\mathnormal{a}}}", @"\mathrm{\pi _{\mathnormal{a}}}")] + [InlineData(@"\pi_\kappa", @"\mathrm{\pi _{\mathnormal{\kappa }}}", @"\mathrm{\pi _{\mathnormal{\kappa }}}")] + [InlineData(@"\pi_{\kappa23}", @"\mathrm{\pi _{\kappa 23}}", @"\mathrm{\pi _{\kappa 23}}")] + [InlineData(@"\pi_{1a\kappa23}", @"\mathrm{\pi _{1a\kappa 23}}", @"\mathrm{\pi _{1a\kappa 23}}")] + [InlineData(@"ii", @"\mathrm{i}\cdot \mathrm{i}", @"-1")] + [InlineData(@"i\infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] + [InlineData(@"\infty\infty", @"\infty \cdot \infty ", @"\infty ")] + [InlineData(@"\theta θ \mathrm{theta\ θ}", @"\theta \theta \theta \theta ", @"\theta ^4")] + [InlineData(@"\theta_\theta\theta_θθ_\mathrm{theta}\mathrm{theta_θ}\mathrm{θ}_{theta}", @"\theta _{\theta }\theta _{\theta }\theta _{\theta }\theta _{\theta }\theta _{\theta }", @"\theta _{\theta }^5")] + public void Variables(string input, string converted, string result) => Test(input, converted, result); + [Theory] + [InlineData("a + b", @"a+b", "a+b")] + [InlineData("a - b", @"a-b", "a-b")] + [InlineData(@"a\times b", @"ab", @"ab")] + [InlineData(@"a\cdot b", @"ab", @"ab")] + [InlineData(@"a / b", @"\frac{a}{b}", @"\frac{a}{b}")] + [InlineData(@"a\div b", @"\frac{a}{b}", @"\frac{a}{b}")] + [InlineData(@"\frac ab", @"\frac{a}{b}", @"\frac{a}{b}")] + [InlineData(@"a + b + c", @"a+b+c", "a+b+c")] + [InlineData(@"a + b - c", @"a+b-c", "a+b-c")] + [InlineData(@"a + b \times c", @"a+bc", @"a+bc")] + [InlineData(@"a + b \cdot c", @"a+bc", @"a+bc")] + [InlineData(@"a + b / c", @"a+\frac{b}{c}", @"a+\frac{b}{c}")] + [InlineData(@"a - b + c", @"a-b+c", "a-b+c")] + [InlineData(@"a - b - c", @"a-b-c", @"a-b-c")] + [InlineData(@"a - b \times c", @"a-bc", @"a-bc")] + [InlineData(@"a - b / c", @"a-\frac{b}{c}", @"a-\frac{b}{c}")] + [InlineData(@"a \times b + c", @"ab+c", @"ab+c")] + [InlineData(@"a \times b - c", @"ab-c", @"ab-c")] + [InlineData(@"a \times b \times c", @"abc", @"abc")] + [InlineData(@"a \times b / c", @"\frac{ab}{c}", @"\frac{ab}{c}")] + [InlineData(@"a \cdot b + c", @"ab+c", @"ab+c")] + [InlineData(@"a \cdot b - c", @"ab-c", @"ab-c")] + [InlineData(@"a \cdot b \times c", @"abc", @"abc")] + [InlineData(@"a \times b \cdot c", @"abc", @"abc")] + [InlineData(@"a \cdot b \cdot c", @"abc", @"abc")] + [InlineData(@"a \cdot bc", @"abc", @"abc")] + [InlineData(@"a \cdot b / c", @"\frac{ab}{c}", @"\frac{ab}{c}")] + [InlineData(@"a / b + c", @"\frac{a}{b}+c", @"\frac{a}{b}+c")] + [InlineData(@"a / b - c", @"\frac{a}{b}-c", @"\frac{a}{b}-c")] + [InlineData(@"a / b \times c", @"\frac{a}{b}\cdot c", @"\frac{a}{b}\cdot c")] + [InlineData(@"a / b \cdot c", @"\frac{a}{b}\cdot c", @"\frac{a}{b}\cdot c")] + [InlineData(@"a / b / c", @"\frac{\frac{a}{b}}{c}", @"\frac{a}{bc}")] + [InlineData(@"2+\frac ab", @"2+\frac{a}{b}", @"2+\frac{a}{b}")] + [InlineData(@"\frac ab+2", @"\frac{a}{b}+2", @"\frac{a}{b}+2")] + [InlineData(@"2-\frac ab", @"2-\frac{a}{b}", @"2-\frac{a}{b}")] + [InlineData(@"\frac ab-2", @"\frac{a}{b}-2", @"\frac{a}{b}-2")] + [InlineData(@"2\times\frac ab", @"2\cdot \frac{a}{b}", @"2\cdot \frac{a}{b}")] + [InlineData(@"2\cdot\frac ab", @"2\cdot \frac{a}{b}", @"2\cdot \frac{a}{b}")] + [InlineData(@"\frac ab\times 2", @"\frac{a}{b}\cdot 2", @"\frac{a}{b}\cdot 2")] + [InlineData(@"\frac ab\cdot 2", @"\frac{a}{b}\cdot 2", @"\frac{a}{b}\cdot 2")] + [InlineData(@"2/\frac ab", @"\frac{2}{\frac{a}{b}}", @"\frac{2b}{a}")] + [InlineData(@"\frac ab/2", @"\frac{\frac{a}{b}}{2}", @"\frac{a}{2b}")] + [InlineData(@"1+\mathrm{i}", @"1+\mathrm{i}", @"1+\mathrm{i}")] + [InlineData(@"1-\mathrm{i}", @"1-\mathrm{i}", @"1-\mathrm{i}")] + [InlineData(@"\mathrm{i}+1", @"\mathrm{i}+1", @"1+\mathrm{i}")] + [InlineData(@"\mathrm{i}-1", @"\mathrm{i}-1", @"-1+\mathrm{i}")] + [InlineData(@"\infty+1", @"\infty +1", @"\infty ")] + [InlineData(@"\infty+\mathrm{i}", @"\infty +\mathrm{i}", @"\infty +\mathrm{i}")] + [InlineData(@"\infty+\infty", @"\infty +\infty ", @"\infty ")] + [InlineData(@"\infty\times \infty", @"\infty \cdot \infty ", @"\infty ")] + [InlineData(@"\infty\cdot \infty", @"\infty \cdot \infty ", @"\infty ")] + [InlineData(@"\mathrm{i}\times \infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] + [InlineData(@"\mathrm{i}\cdot \infty", @"\mathrm{i}\cdot \infty ", @"\infty \mathrm{i}")] + [InlineData(@"\infty\times \mathrm{i}", @"\infty \cdot \mathrm{i}", @"\infty \mathrm{i}")] + [InlineData(@"\mathrm{i}\times \mathrm{i}", @"\mathrm{i}\cdot \mathrm{i}", @"-1")] + [InlineData(@"\frac00", @"\frac{0}{0}", @"\mathrm{undefined}")] + [InlineData(@"\frac xx", @"\frac{x}{x}", @"1\quad \mathrm{for}\quad x\neq 0")] + [InlineData(@"\frac xx+1", @"\frac{x}{x}+1", @"2\quad \mathrm{for}\quad x\neq 0")] + [InlineData(@"\frac0\infty", @"\frac{0}{\infty }", @"0")] + [InlineData(@"\frac2\infty", @"\frac{2}{\infty }", @"0")] + [InlineData(@"\frac{-2}\infty", @"\frac{-2}{\infty }", @"0")] + [InlineData(@"\frac20", @"\frac{2}{0}", @"\mathrm{undefined}")] + [InlineData(@"\frac{-2}0", @"\frac{-2}{0}", @"\mathrm{undefined}")] + [InlineData(@"\frac\infty0", @"\frac{\infty }{0}", @"\mathrm{undefined}")] + [InlineData(@"\frac{-\infty}0", @"\frac{-\infty }{0}", @"\mathrm{undefined}")] + [InlineData(@"\frac\infty\infty", @"\frac{\infty }{\infty }", @"\mathrm{undefined}")] + [InlineData(@"\frac{-\infty}{\infty}", @"\frac{-\infty }{\infty }", @"\mathrm{undefined}")] + [InlineData(@"a / bc", @"\frac{a}{bc}", @"\frac{a}{bc}")] + [InlineData(@"a / bc / d", @"\frac{\frac{a}{bc}}{d}", @"\frac{a}{bcd}")] + [InlineData(@"-2/\sin x/y", @"\frac{\frac{-2}{\sin \left( x\right) }}{y}", @"\frac{-2}{\sin \left( x\right) \cdot y}")] + public void BinaryOperators(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"+i", @"\mathrm{i}", @"\mathrm{i}")] + [InlineData(@"-i", @"-\mathrm{i}", @"-\mathrm{i}")] + [InlineData("+a", "a", "a")] + [InlineData("-a", "-a", "-a")] + [InlineData("++a", "a", "a")] + [InlineData("+-a", "-a", "-a")] + [InlineData("-+a", "-a", "-a")] + [InlineData("--a", @"-\left( -1\right) \cdot a", "a")] + [InlineData("+++a", "a", "a")] + [InlineData("---a", @"-\left( -1\right) \left( -1\right) \cdot a", "-a")] + [InlineData("a++a", "a+a", @"2a")] + [InlineData("a+-a", "a-a", "0")] + [InlineData("a-+a", "a-a", "0")] + [InlineData("a--a", @"a--a", @"2a")] + [InlineData("a+++a", "a+a", @"2a")] + [InlineData("a---a", @"a--\left( -1\right) \cdot a", "0")] + [InlineData(@"a\times+a", @"aa", "a^2")] + [InlineData(@"a\times-a", @"a\left( -1\right) \cdot a", "-a^2")] + [InlineData(@"+a\times+a", @"aa", "a^2")] + [InlineData(@"-a\times-a", @"-a\left( -1\right) \cdot a", "a^2")] + [InlineData("a/+a", @"\frac{a}{a}", @"1\quad \mathrm{for}\quad a\neq 0")] + [InlineData("a/-a", @"\frac{a}{-a}", @"-1\quad \mathrm{for}\quad a\neq 0")] + [InlineData("+a/+a", @"\frac{a}{a}", @"1\quad \mathrm{for}\quad a\neq 0")] + [InlineData("-a/-a", @"\frac{-a}{-a}", @"1\quad \mathrm{for}\quad a\neq 0")] + [InlineData(@"-2+-2+-2", @"-2-2-2", "-6")] + [InlineData(@"-2--2--2", @"-2--2--2", "2")] + [InlineData(@"-2\times -2\times -2", @"-2\left( -1\right) \cdot 2\left( -1\right) \cdot 2", "-8")] + [InlineData(@"-2/-2/-2", @"\frac{\frac{-2}{-2}}{-2}", @"\frac{-1}{2}")] + public void UnaryOperators(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"9\%", @"\frac{9}{100}", @"\frac{9}{100}")] + [InlineData(@"a\%", @"\frac{a}{100}", @"\frac{a}{100}")] + [InlineData(@"\pi\%", @"\frac{\mathrm{\pi }}{100}", @"\frac{\mathrm{\pi }}{100}")] + [InlineData(@"a\%\%", @"\frac{\frac{a}{100}}{100}", @"\frac{a}{10000}")] + [InlineData(@"9\%+3", @"\frac{9}{100}+3", @"\frac{309}{100}")] + [InlineData(@"-9\%+3", @"-\frac{9}{100}+3", @"\frac{291}{100}")] + [InlineData(@"2^2\%", @"\frac{2^2}{100}", @"\frac{1}{25}")] + [InlineData(@"2\%^2", @"\left( \frac{2}{100}\right) ^2", @"\frac{1}{2500}")] + [InlineData(@"2\%2", @"\frac{2}{100}\cdot 2", @"\frac{1}{25}")] + [InlineData(@"1+2\%^2", @"1+\left( \frac{2}{100}\right) ^2", @"\frac{2501}{2500}")] + [InlineData(@"9\degree", @"\frac{9\mathrm{\pi }}{180}", @"\frac{\mathrm{\pi }}{20}")] + [InlineData(@"a\degree", @"\frac{a\mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi }}{180}")] + [InlineData(@"\pi\degree", @"\frac{\mathrm{\pi }\cdot \mathrm{\pi }}{180}", @"\frac{\mathrm{\pi ^{\mathnormal{2}}}}{180}")] + [InlineData(@"a\%\degree", @"\frac{\frac{a}{100}\cdot \mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi }}{18000}")] + [InlineData(@"a\degree\degree", @"\frac{\frac{a\mathrm{\pi }}{180}\cdot \mathrm{\pi }}{180}", @"\frac{a\mathrm{\pi ^{\mathnormal{2}}}}{32400}")] + [InlineData(@"9\degree+3", @"\frac{9\mathrm{\pi }}{180}+3", @"3+\frac{\mathrm{\pi }}{20}")] + [InlineData(@"-9\degree+3", @"-\frac{9\mathrm{\pi }}{180}+3", @"3+\frac{-1}{20}\cdot \mathrm{\pi }")] + [InlineData(@"2^2\degree", @"\frac{2^2\cdot \mathrm{\pi }}{180}", @"\frac{\mathrm{\pi }}{45}")] + [InlineData(@"2\degree^2", @"\left( \frac{2\mathrm{\pi }}{180}\right) ^2", @"\frac{\mathrm{\pi ^{\mathnormal{2}}}}{8100}")] + [InlineData(@"2\degree2", @"\frac{2\mathrm{\pi }}{180}\cdot 2", @"\frac{\mathrm{\pi }}{45}")] + [InlineData(@"1+2\degree^2", @"1+\left( \frac{2\mathrm{\pi }}{180}\right) ^2", @"1+\frac{\mathrm{\pi ^{\mathnormal{2}}}}{8100}")] + [InlineData(@"0!", @"0!", @"1")] + [InlineData(@"2.5!", @"\left( \frac{5}{2}\right) !", @"\frac{15}{8}\cdot \sqrt{\mathrm{\pi }}")] + [InlineData(@"-1!", @"-1!", @"-1")] + [InlineData(@"(-1)!", @"\left( -1\right) !", @"\mathrm{undefined}")] + [InlineData(@"(-1)!!", @"2^{\frac{-1}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }\left( -1\right) \cdot 1\right) }{4}}\cdot \left( \frac{-1}{2}\right) !", @"1")] + [InlineData(@"(-1)!!!", @"\left( 2^{\frac{-1}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }\left( -1\right) \cdot 1\right) }{4}}\cdot \left( \frac{-1}{2}\right) !\right) !", @"1")] + public void PostfixOperators(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData("0", "1")] + [InlineData("1", "1")] + [InlineData("2", "2")] + [InlineData("3", "3")] + [InlineData("30", "42849873690624000")] + [InlineData("31", "191898783962510625")] + [InlineData(@"\frac{25}{2}", @"\sqrt[4]{2}^{25}\sqrt[4]{\frac{2}{\mathrm{\pi }}}\cdot \left( \frac{25}{4}\right) !")] + public void DoubleFactorial(string x, string result) => Test($"{x}!!", $$$"""2^{\frac{{{{x}}}}{2}}\left( \frac{2}{\mathrm{\pi }}\right) ^{\frac{1-\cos \left( \mathrm{\pi }{{{(x == @"\frac{25}{2}" ? "" : @"\cdot ")}}}{{{x}}}\right) }{4}}\cdot \left( \frac{{{{x}}}}{2}\right) !""", result); + [Theory] + [InlineData(@"2^2", "2^2", "4")] + [InlineData(@".2^2", @"\left( \frac{1}{5}\right) ^2", @"\frac{1}{25}")] + [InlineData(@"2.^2", "2^2", "4")] + [InlineData(@"2.1^2", @"\left( \frac{21}{10}\right) ^2", @"\frac{441}{100}")] + [InlineData(@"a^a", "a^a", "a^a")] + [InlineData(@"a^{a+b}", "a^{a+b}", "a^{a+b}")] + [InlineData(@"a^{-2}", "a^{-2}", @"\frac{1}{a^2}")] + [InlineData(@"2^{3^4}", "2^{3^4}", "2417851639229258349412352")] + [InlineData(@"4^{3^2}", "4^{3^2}", "262144")] + [InlineData(@"4^3+2", "4^3+2", "66")] + [InlineData(@"2+3^4", "2+3^4", "83")] + [InlineData(@"4^3\cdot2", @"4^3\cdot 2", "128")] + [InlineData(@"2\times3^4", @"2\cdot 3^4", "162")] + [InlineData(@"1/x", @"\frac{1}{x}", @"\frac{1}{x}")] + [InlineData(@"2/x", @"\frac{2}{x}", @"\frac{2}{x}")] + [InlineData(@"0^x", @"0^x", @"0\quad \mathrm{for}\quad \left( 1+\frac{1}{\operatorname{sgn} \left( x\right) ^2}\right) \cdot x>0")] + [InlineData(@"1^x", @"1^x", @"1")] + [InlineData(@"x^0", @"x^0", @"1\quad \mathrm{for}\quad x\neq 0")] + [InlineData(@"x^{-1}", @"x^{-1}", @"\frac{1}{x}")] + [InlineData(@"-i^{-1}", @"-\mathrm{i^{\mathnormal{-1}}}", @"\mathrm{i}")] + [InlineData(@"i^{-2}", @"\mathrm{i^{\mathnormal{-2}}}", @"-1")] + [InlineData(@"i^{-1}", @"\mathrm{i^{\mathnormal{-1}}}", @"-\mathrm{i}")] + [InlineData(@"i^0", @"\mathrm{i^{\mathnormal{0}}}", @"1")] + [InlineData(@"i^1", @"\mathrm{i^{\mathnormal{1}}}", @"\mathrm{i}")] + [InlineData(@"i^2", @"\mathrm{i^{\mathnormal{2}}}", @"-1")] + [InlineData(@"i^3", @"\mathrm{i^{\mathnormal{3}}}", @"-\mathrm{i}")] + [InlineData(@"i^4", @"\mathrm{i^{\mathnormal{4}}}", @"1")] + [InlineData(@"i^5", @"\mathrm{i^{\mathnormal{5}}}", @"\mathrm{i}")] + [InlineData(@"\mathrm{-i^{\mathit -1}}", @"-\mathrm{i^{\mathnormal{-1}}}", @"\mathrm{i}")] + [InlineData(@"\mathrm{i^{-2}}", @"\mathrm{i^{\mathnormal{-2}}}", @"-1")] + [InlineData(@"\mathrm i^{-1}", @"\mathrm{i^{\mathnormal{-1}}}", @"-\mathrm{i}")] + [InlineData(@"\mathrm{i^0}", @"\mathrm{i^{\mathnormal{0}}}", @"1")] + [InlineData(@"\mathrm{i^1}", @"\mathrm{i^{\mathnormal{1}}}", @"\mathrm{i}")] + [InlineData(@"\mathrm{i^{\mathnormal 2}}", @"\mathrm{i^{\mathnormal{2}}}", @"-1")] + [InlineData(@"\mathrm{i^3}", @"\mathrm{i^{\mathnormal{3}}}", @"-\mathrm{i}")] + [InlineData(@"\mathrm{i^{\mathnormal{4}}}", @"\mathrm{i^{\mathnormal{4}}}", @"1")] + [InlineData(@"\mathrm{i^5}", @"\mathrm{i^{\mathnormal{5}}}", @"\mathrm{i}")] + [InlineData(@"\mathrm{zz^2}", @"\mathrm{zz^{\mathnormal{2}}}", @"\mathrm{zz^{\mathnormal{2}}}")] + [InlineData(@"\mathrm{zz^{aa}}", @"\mathrm{zz^{aa}}", @"\mathrm{zz^{aa}}")] + [InlineData(@"\mathrm{zz^{aa\mathnormal{a}}}", @"\mathrm{zz^{aa\mathnormal{a}}}", @"\mathrm{zz^{\mathnormal{a}aa}}")] + [InlineData(@"\mathrm{zz^{aa\cdot aa}}", @"\mathrm{zz^{aa\mathnormal{\cdot }aa}}", @"\mathrm{zz^{aa^{\mathnormal{2}}}}")] + [InlineData(@"\mathrm{a^2a^2ab}", @"a^2a^2\cdot \mathrm{ab}", @"a^4\cdot \mathrm{ab}")] + [InlineData(@"\mathrm{a_2a_2ab}", @"a_2a_2\mathrm{ab}", @"a_2^2\cdot \mathrm{ab}")] + [InlineData(@"\mathrm{a^2_2a_2^2ab}", @"a_2^2a_2^2\cdot \mathrm{ab}", @"a_2^4\cdot \mathrm{ab}")] + [InlineData(@"\mathrm{ab^2ab_2ab}", @"\mathrm{ab^{\mathnormal{2}}}\cdot \mathrm{ab_{\mathnormal{2}}}\cdot \mathrm{ab}", @"\mathrm{ab^{\mathnormal{3}}}\cdot \mathrm{ab_{\mathnormal{2}}}")] + [InlineData(@"10^2", @"10^2", @"100")] + [InlineData(@".1^2", @"\left( \frac{1}{10}\right) ^2", @"\frac{1}{100}")] + [InlineData(@"10^x", @"10^x", @"10^x")] + [InlineData(@"{\frac 12}^4", @"\left( \frac{1}{2}\right) ^4", @"\frac{1}{16}")] + [InlineData(@"\sqrt2", @"\sqrt{2}", @"\sqrt{2}")] + [InlineData(@"\sqrt2^2", @"\left( \sqrt{2}\right) ^2", "2")] + [InlineData(@"\sqrt[3]2", @"2^{\frac{1}{3}}", @"\sqrt[3]{2}")] + [InlineData(@"\sqrt[3/2]2", @"2^{\frac{1}{\frac{3}{2}}}", @"\sqrt[3]{2}^2")] + [InlineData(@"\sqrt[3]2^3", @"\left( 2^{\frac{1}{3}}\right) ^3", "2")] + [InlineData(@"\sqrt[3]2^{1+1+1}", @"\left( 2^{\frac{1}{3}}\right) ^{1+1+1}", "2")] + [InlineData(@"\sqrt[1+1+1]2^{1+1+1}", @"\left( 2^{\frac{1}{1+1+1}}\right) ^{1+1+1}", "2")] + public void Exponents(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\sin x", @"\sin \left( x\right) ", @"\sin \left( x\right) ")] + [InlineData(@"\cos x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] + [InlineData(@"\tan x", @"\tan \left( x\right) ", @"\tan \left( x\right) ")] + [InlineData(@"\cot x", @"\cot \left( x\right) ", @"\cot \left( x\right) ")] + [InlineData(@"\sec x", @"\sec \left( x\right) ", @"\sec \left( x\right) ")] + [InlineData(@"\csc x", @"\csc \left( x\right) ", @"\csc \left( x\right) ")] + [InlineData(@"\arcsin x", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\arccos x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] + [InlineData(@"\arctan x", @"\arctan \left( x\right) ", @"\arctan \left( x\right) ")] + [InlineData(@"\arccot x", @"\arccot \left( x\right) ", @"\arccot \left( x\right) ")] + [InlineData(@"\arcsec x", @"\arcsec \left( x\right) ", @"\arcsec \left( x\right) ")] + [InlineData(@"\arccsc x", @"\arccsc \left( x\right) ", @"\arccsc \left( x\right) ")] + [InlineData(@"\ln x", @"\ln \left( x\right) ", @"\ln \left( x\right) ")] + [InlineData(@"\log x", @"\log \left( x\right) ", @"\log \left( x\right) ")] + [InlineData(@"\log_3 x", @"\log _3\left( x\right) ", @"\log _3\left( x\right) ")] + [InlineData(@"\log_{10} x", @"\log \left( x\right) ", @"\log \left( x\right) ")] + [InlineData(@"\log_e x", @"\ln \left( x\right) ", @"\ln \left( x\right) ")] + [InlineData(@"\ln x^2", @"\ln \left( x^2\right) ", @"\ln \left( x^2\right) ")] + [InlineData(@"\log x^2", @"\log \left( x^2\right) ", @"\log \left( x^2\right) ")] + [InlineData(@"\log_{10} x^2", @"\log \left( x^2\right) ", @"\log \left( x^2\right) ")] + [InlineData(@"\log_3 x^2", @"\log _3\left( x^2\right) ", @"\log _3\left( x^2\right) ")] + [InlineData(@"\log_e x^2", @"\ln \left( x^2\right) ", @"\ln \left( x^2\right) ")] + [InlineData(@"\ln x^{-1}", @"\ln \left( x^{-1}\right) ", @"\ln \left( \frac{1}{x}\right) ")] + [InlineData(@"\log x^{-1}", @"\log \left( x^{-1}\right) ", @"\log \left( \frac{1}{x}\right) ")] + [InlineData(@"\log_{10} x^{-1}", @"\log \left( x^{-1}\right) ", @"\log \left( \frac{1}{x}\right) ")] + [InlineData(@"\log_3 x^{-1}", @"\log _3\left( x^{-1}\right) ", @"\log _3\left( \frac{1}{x}\right) ")] + [InlineData(@"\log_e x^{-1}", @"\ln \left( x^{-1}\right) ", @"\ln \left( \frac{1}{x}\right) ")] + [InlineData(@"2\sin x", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] + [InlineData(@"\sin 2x", @"\sin \left( 2x\right) ", @"\sin \left( 2x\right) ")] + [InlineData(@"\sin x2", @"\sin \left( x\cdot 2\right) ", @"\sin \left( 2x\right) ")] + [InlineData(@"\sin x2^2", @"\sin \left( x\cdot 2^2\right) ", @"\sin \left( 4x\right) ")] + [InlineData(@"\sin xy", @"\sin \left( xy\right) ", @"\sin \left( xy\right) ")] + [InlineData(@"\sin \frac\pi2", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) ", @"1")] + [InlineData(@"\sin \frac\pi2+1", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) +1", @"2")] + [InlineData(@"\cos +x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] + [InlineData(@"\cos -x", @"\cos \left( -x\right) ", @"\cos \left( -x\right) ")] + [InlineData(@"\tan x\%", @"\tan \left( \frac{x}{100}\right) ", @"\tan \left( \frac{x}{100}\right) ")] + [InlineData(@"\tan x\%^2", @"\tan \left( \left( \frac{x}{100}\right) ^2\right) ", @"\tan \left( \left( \frac{x}{100}\right) ^2\right) ")] + [InlineData(@"\cot x\times y", @"\cot \left( x\right) \cdot y", @"\cot \left( x\right) \cdot y")] + [InlineData(@"\cot x/y", @"\frac{\cot \left( x\right) }{y}", @"\frac{\cot \left( x\right) }{y}")] + [InlineData(@"\cos \arccos x", @"\cos \left( \arccos \left( x\right) \right) ", @"x")] + [InlineData(@"\sin^2 x", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] + [InlineData(@"\sin^2 xy+\cos^2 yx", @"\sin \left( xy\right) ^2+\cos \left( yx\right) ^2", @"1")] + [InlineData(@"\log^2 x", @"\log \left( x\right) ^2", @"\log \left( x\right) ^2")] + [InlineData(@"\ln^2 x", @"\ln \left( x\right) ^2", @"\ln \left( x\right) ^2")] + [InlineData(@"\log_{10}^2 x", @"\log \left( x\right) ^2", @"\log \left( x\right) ^2")] + [InlineData(@"\log_3^2 x", @"\log _3\left( x\right) ^2", @"\log _3\left( x\right) ^2")] + public void Functions(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\sin^{-1} x", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\cos^{-1} x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] + [InlineData(@"\tan^{-1} x", @"\arctan \left( x\right) ", @"\arctan \left( x\right) ")] + [InlineData(@"\cot^{-1} x", @"\arccot \left( x\right) ", @"\arccot \left( x\right) ")] + [InlineData(@"\sec^{-1} x", @"\arcsec \left( x\right) ", @"\arcsec \left( x\right) ")] + [InlineData(@"\csc^{-1} x", @"\arccsc \left( x\right) ", @"\arccsc \left( x\right) ")] + [InlineData(@"\arcsin^{-1} x", @"\sin \left( x\right) ", @"\sin \left( x\right) ")] + [InlineData(@"\arccos^{-1} x", @"\cos \left( x\right) ", @"\cos \left( x\right) ")] + [InlineData(@"\arctan^{-1} x", @"\tan \left( x\right) ", @"\tan \left( x\right) ")] + [InlineData(@"\arccot^{-1} x", @"\cot \left( x\right) ", @"\cot \left( x\right) ")] + [InlineData(@"\arcsec^{-1} x", @"\sec \left( x\right) ", @"\sec \left( x\right) ")] + [InlineData(@"\arccsc^{-1} x", @"\csc \left( x\right) ", @"\csc \left( x\right) ")] + [InlineData(@"\ln^{-1} x", @"\mathrm{e^{\mathnormal{x}}}", @"\mathrm{e^{\mathnormal{x}}}")] + [InlineData(@"\log^{-1} x", @"10^x", @"10^x")] + [InlineData(@"\log_3^{-1} x", @"3^x", @"3^x")] + [InlineData(@"\log^{-1}_{10} x", @"10^x", @"10^x")] + [InlineData(@"\log_e^{-1} x", @"\mathrm{e^{\mathnormal{x}}}", @"\mathrm{e^{\mathnormal{x}}}")] + [InlineData(@"\ln^{-1} x^2", @"\mathrm{e^{\mathnormal{x^2}}}", @"\mathrm{e^{\mathnormal{x^2}}}")] + [InlineData(@"\log^{-1} x^2", @"10^{x^2}", @"10^{x^2}")] + [InlineData(@"\log_{10}^{-1} x^2", @"10^{x^2}", @"10^{x^2}")] + [InlineData(@"\log_3^{-1} x^2", @"3^{x^2}", @"3^{x^2}")] + [InlineData(@"\log_e^{-1} x^2", @"\mathrm{e^{\mathnormal{x^2}}}", @"\mathrm{e^{\mathnormal{x^2}}}")] + [InlineData(@"\ln^{-1} x^{-1}", @"\mathrm{e^{\mathnormal{x^{-1}}}}", @"\mathrm{e^{\mathnormal{\frac{1}{x}}}}")] + [InlineData(@"\log^{-1} x^{-1}", @"10^{x^{-1}}", @"10^{\frac{1}{x}}")] + [InlineData(@"\log_{10}^{-1} x^{-1}", @"10^{x^{-1}}", @"10^{\frac{1}{x}}")] + [InlineData(@"\log_3^{-1} x^{-1}", @"3^{x^{-1}}", @"3^{\frac{1}{x}}")] + [InlineData(@"\log_e^{-1} x^{-1}", @"\mathrm{e^{\mathnormal{x^{-1}}}}", @"\mathrm{e^{\mathnormal{\frac{1}{x}}}}")] + [InlineData(@"2\sin^{-1} x", @"2\arcsin \left( x\right) ", @"2\arcsin \left( x\right) ")] + [InlineData(@"\sin^{-1} 2x", @"\arcsin \left( 2x\right) ", @"\arcsin \left( 2x\right) ")] + [InlineData(@"\sin^{-1} xy", @"\arcsin \left( xy\right) ", @"\arcsin \left( xy\right) ")] + [InlineData(@"\cos^{-1} +x", @"\arccos \left( x\right) ", @"\arccos \left( x\right) ")] + [InlineData(@"\cos^{-1} -x", @"\arccos \left( -x\right) ", @"\arccos \left( -x\right) ")] + [InlineData(@"\tan^{-1} x\%", @"\arctan \left( \frac{x}{100}\right) ", @"\arctan \left( \frac{x}{100}\right) ")] + [InlineData(@"\tan^{-1} x\%^2", @"\arctan \left( \left( \frac{x}{100}\right) ^2\right) ", @"\arctan \left( \left( \frac{x}{100}\right) ^2\right) ")] + [InlineData(@"\cot^{-1} x\times y", @"\arccot \left( x\right) \cdot y", @"\arccot \left( x\right) \cdot y")] + [InlineData(@"\cot^{-1} x/y", @"\frac{\arccot \left( x\right) }{y}", @"\frac{\arccot \left( x\right) }{y}")] + [InlineData(@"\cos^{-1} \arccos^{-1} x", @"\arccos \left( \cos \left( x\right) \right) ", @"x")] + [InlineData(@"\sin^1 x", @"\sin \left( x\right) ^1", @"\sin \left( x\right) ")] + [InlineData(@"\sin^{+1} x", @"\sin \left( x\right) ^1", @"\sin \left( x\right) ")] + [InlineData(@"\sin^{+-1} x", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin^{-+1} x", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin^{--1} x", @"\sin \left( x\right) ^{-\left( -1\right) \cdot 1}", @"\sin \left( x\right) ")] + [InlineData(@"\sin^{-1^2} x", @"\sin \left( x\right) ^{-1^2}", @"\csc \left( x\right) ")] + [InlineData(@"\sin^{-1+3} xy+\cos^{-1+3} yx", @"\sin \left( xy\right) ^{-1+3}+\cos \left( yx\right) ^{-1+3}", @"1")] + [InlineData(@"\log^{-a_2} x", @"\log \left( x\right) ^{-a_2}", @"\log \left( x\right) ^{-a_2}")] + [InlineData(@"\ln^{3-1} x", @"\ln \left( x\right) ^{3-1}", @"\ln \left( x\right) ^2")] + public void FunctionInverses(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"1+(2+3)", @"1+2+3", @"6")] + [InlineData(@"1+((2+3))", @"1+2+3", @"6")] + [InlineData(@"2\times(3+4)", @"2\left( 3+4\right) ", @"14")] + [InlineData(@"(3+4)\times2", @"\left( 3+4\right) \cdot 2", @"14")] + [InlineData(@"(5+6)^2", @"\left( 5+6\right) ^2", @"121")] + [InlineData(@"(5+6)", @"5+6", @"11")] + [InlineData(@"((5+6))", @"5+6", @"11")] + [InlineData(@"(5+6)2", @"\left( 5+6\right) \cdot 2", @"22")] + [InlineData(@"2(5+6)", @"2\left( 5+6\right) ", @"22")] + [InlineData(@"2(5+6)2", @"2\left( 5+6\right) \cdot 2", @"44")] + [InlineData(@"(5+6)x", @"\left( 5+6\right) \cdot x", @"11x")] + [InlineData(@"x(5+6)", @"x\left( 5+6\right) ", @"11x")] + [InlineData(@"x(5+6)x", @"x\left( 5+6\right) \cdot x", @"11x^2")] + [InlineData(@"(5+6).2", @"\left( 5+6\right) \cdot \frac{1}{5}", @"\frac{11}{5}")] + [InlineData(@".2(5+6)", @"\frac{1}{5}\left( 5+6\right) ", @"\frac{11}{5}")] + [InlineData(@".2(5+6).2", @"\frac{1}{5}\left( 5+6\right) \cdot \frac{1}{5}", @"\frac{11}{25}")] + [InlineData(@"(5+6)2.", @"\left( 5+6\right) \cdot 2", @"22")] + [InlineData(@"2.(5+6)", @"2\left( 5+6\right) ", @"22")] + [InlineData(@"2.(5+6)2.", @"2\left( 5+6\right) \cdot 2", @"44")] + [InlineData(@"(5+6)(2)", @"\left( 5+6\right) \cdot 2", @"22")] + [InlineData(@"(5+6)(1+1)", @"\left( 5+6\right) \left( 1+1\right) ", @"22")] + [InlineData(@"(5+6)(-(-2))", @"\left( 5+6\right) \left( -1\right) \left( -1\right) \cdot 2", @"22")] + [InlineData(@"(5+6)(--2)", @"\left( 5+6\right) \left( -1\right) \left( -1\right) \cdot 2", @"22")] + [InlineData(@"+(1)", @"1", @"1")] + [InlineData(@"+(1)\%", @"\frac{1}{100}", @"\frac{1}{100}")] + [InlineData(@"+(-1)", @"-1", @"-1")] + [InlineData(@"-(+1)", @"-1", @"-1")] + [InlineData(@"-(-1)", @"-\left( -1\right) \cdot 1", @"1")] + [InlineData(@"--(--1)", @"-\left( -1\right) \left( -1\right) \left( -1\right) \cdot 1", @"1")] + [InlineData(@"(2+3)^{(4+5)}", @"\left( 2+3\right) ^{4+5}", @"1953125")] + [InlineData(@"(2+3)^{((4)+5)}", @"\left( 2+3\right) ^{4+5}", @"1953125")] + [InlineData(@"(1+i)\infty", @"\left( 1+\mathrm{i}\right) \cdot \infty ", @"\infty +\infty \mathrm{i}")] + [InlineData(@"2\sin(x)", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] + [InlineData(@"(2)\sin(x)", @"2\sin \left( x\right) ", @"2\sin \left( x\right) ")] + [InlineData(@"\sin(x+1)", @"\sin \left( x+1\right) ", @"\sin \left( 1+x\right) ")] + [InlineData(@"\sin((x+1))", @"\sin \left( x+1\right) ", @"\sin \left( 1+x\right) ")] + [InlineData(@"\sin(2(x+1))", @"\sin \left( 2\left( x+1\right) \right) ", @"\sin \left( 2\left( 1+x\right) \right) ")] + [InlineData(@"\sin((x+1)+2)", @"\sin \left( x+1+2\right) ", @"\sin \left( 3+x\right) ")] + [InlineData(@"\sin(x)2", @"\sin \left( x\right) \cdot 2", @"2\sin \left( x\right) ")] + [InlineData(@"\sin(x)(x+1)", @"\sin \left( x\right) \left( x+1\right) ", @"\sin \left( x\right) \left( 1+x\right) ")] + [InlineData(@"\sin(x)(x+1)(x)", @"\sin \left( x\right) \left( x+1\right) \cdot x", @"\sin \left( x\right) \left( 1+x\right) \cdot x")] + [InlineData(@"\sin(x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] + [InlineData(@"\sin\ (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] + [InlineData(@"\sin\; (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] + [InlineData(@"\sin\ \; (x^2)", @"\sin \left( x^2\right) ", @"\sin \left( x^2\right) ")] + [InlineData(@"\sin^3(x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] + [InlineData(@"\sin^3\ (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] + [InlineData(@"\sin^3\; (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] + [InlineData(@"\sin^3\ \; (x)", @"\sin \left( x\right) ^3", @"\sin \left( x\right) ^3")] + [InlineData(@"\sin^{-1}(x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\sin^{-1}\ (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\sin^{-1}\; (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\sin^{-1}\ \; (x)", @"\arcsin \left( x\right) ", @"\arcsin \left( x\right) ")] + [InlineData(@"\sin(x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] + [InlineData(@"\sin\ (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] + [InlineData(@"\sin\; (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] + [InlineData(@"\sin\ \; (x)^2", @"\sin \left( x\right) ^2", @"\sin \left( x\right) ^2")] + [InlineData(@"\sin(x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin\ (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin\; (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin\ \; (x)^{-1}", @"\sin \left( x\right) ^{-1}", @"\csc \left( x\right) ")] + [InlineData(@"\sin^3(x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] + [InlineData(@"\sin^3\ (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] + [InlineData(@"\sin^3\; (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] + [InlineData(@"\sin^3\ \; (x)^{-1}", @"\sin \left( x\right) ^{3\left( -1\right) \cdot 1}", @"\frac{1}{\sin \left( x\right) ^3}")] + [InlineData(@"\sin^{-1}(x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] + [InlineData(@"\sin^{-1}\ (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] + [InlineData(@"\sin^{-1}\; (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] + [InlineData(@"\sin^{-1}\ \; (x)^{-1}", @"\arcsin \left( x\right) ^{-1}", @"\frac{1}{\arcsin \left( x\right) }")] + [InlineData(@"\sin^a(x)", @"\sin \left( x\right) ^a", @"\sin \left( x\right) ^a")] + [InlineData(@"\sin^a(x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] + [InlineData(@"\sin^a\ (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] + [InlineData(@"\sin^a\; (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] + [InlineData(@"\sin^a\ \; (x)^2", @"\sin \left( x\right) ^{a\cdot 2}", @"\sin \left( x\right) ^{2a}")] + [InlineData(@"\sin(x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] + [InlineData(@"\sin\ (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] + [InlineData(@"\sin\; (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] + [InlineData(@"\sin\ \; (x)^2(x)", @"\sin \left( x\right) ^2\cdot x", @"\sin \left( x\right) ^2\cdot x")] + [InlineData(@"\sin^a(x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] + [InlineData(@"\sin^a\ (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] + [InlineData(@"\sin^a\; (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] + [InlineData(@"\sin^a\ \; (x)^2(x)", @"\sin \left( x\right) ^{a\cdot 2}\cdot x", @"\sin \left( x\right) ^{2a}\cdot x")] + [InlineData(@"\sin (\frac\pi2)", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) ", @"1")] + [InlineData(@"\sin (\frac\pi2)+1", @"\sin \left( \frac{\mathrm{\pi }}{2}\right) +1", @"2")] + public void Parentheses(string latex, string converted, string result) { + Test(latex, converted, result); + Test(latex.Replace('(', '[').Replace(')', ']'), converted, result); + } + [Theory] + [InlineData(@"\begin{matrix}1\end{matrix}", @"\left[ \begin{matrix}1\end{matrix}\right] ", @"1")] + [InlineData(@"\begin{matrix}1&2\\3&4\end{matrix}", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ")] + [InlineData(@"(\begin{matrix}1\end{matrix})^2", @"\left[ \begin{matrix}1\end{matrix}\right] ^2", @"1")] + [InlineData(@"[\begin{matrix}1&2\end{matrix}]^2", @"\left[ \begin{matrix}1&2\end{matrix}\right] ^2", @"\left[ \begin{matrix}1&4\end{matrix}\right] ")] + [InlineData(@"[\begin{matrix}3\\4\end{matrix}]^2", @"\left[ \begin{matrix}3\\ 4\end{matrix}\right] ^2", @"\left[ \begin{matrix}9\\ 16\end{matrix}\right] ")] + [InlineData(@"[\begin{matrix}1&2\\3&4\end{matrix}]^2", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] ^2", @"\left[ \begin{matrix}7&10\\ 15&22\end{matrix}\right] ")] + [InlineData(@"\begin{matrix}1&2\\3&4\end{matrix}+\begin{matrix}1&2\\3&5\end{matrix}", @"\left[ \begin{matrix}1&2\\ 3&4\end{matrix}\right] +\left[ \begin{matrix}1&2\\ 3&5\end{matrix}\right] ", @"\left[ \begin{matrix}2&4\\ 6&9\end{matrix}\right] ")] + public void Matrices(string latex, string converted, string result) { + Test(latex, converted, result); + Test(latex.Replace("matrix", "pmatrix"), converted, result); + Test(latex.Replace("matrix", "bmatrix"), converted, result); + } + [Theory] + [InlineData(@"1,2", @"1,2")] + [InlineData(@"1,2,3", @"1,2,3")] + [InlineData(@"a,b,c,d", @"a,b,c,d")] + [InlineData(@"\sqrt2,\sqrt[3]2,\frac34", @"\sqrt{2},2^{\frac{1}{3}},\frac{3}{4}")] + [InlineData(@"\sin a,\cos b^2,\tan c_3,\cot de,\sec 12f,\csc g+h", + @"\sin \left( a\right) ,\cos \left( b^2\right) ,\tan \left( c_3\right) ,\cot \left( d\mathrm{e}\right) ,\sec \left( 12f\right) ,\csc \left( g\right) +h")] + public void Comma(string latex, string converted) => Test(latex, converted, null); + [Theory] + [InlineData(@"\emptyset", @"\emptyset ")] + [InlineData(@"\mathbb Z", @"\mathbb{Z}")] + [InlineData(@"\mathbb Q", @"\mathbb{Q}")] + [InlineData(@"\mathbb R", @"\mathbb{R}")] + [InlineData(@"\mathbb C", @"\mathbb{C}")] + [InlineData(@"\{\}", @"\emptyset ")] + [InlineData(@"\left\{\right\}", @"\emptyset ")] + [InlineData(@"\left\{1\right\}", @"\left\{ 1\right\} ")] + [InlineData(@"\{1\}", @"\left\{ 1\right\} ")] + [InlineData(@"\{1,2\}", @"\left\{ 1,2\right\} ")] + [InlineData(@"\{x,y\}", @"\left\{ x,y\right\} ")] + [InlineData(@"\{\sqrt[3]2,\frac34,\sin^2x\}", @"\left\{ 2^{\frac{1}{3}},\frac{3}{4},\sin \left( x\right) ^2\right\} ")] + [InlineData(@"\{\{\}\}", @"\left\{ \emptyset \right\} ")] + [InlineData(@"\{\left\{\right\}\}", @"\left\{ \emptyset \right\} ")] + public void Sets(string latex, string converted) => Test(latex, converted, null); + [Theory] + [InlineData(@"\emptyset\cup\{2\}", @"\emptyset \cup \left\{ 2\right\} ", @"\left\{ 2\right\} ")] + [InlineData(@"\{1\}\cup\{2\}", @"\left\{ 1\right\} \cup \left\{ 2\right\} ", @"\left\{ 1,2\right\} ")] + [InlineData(@"\{3,4\}\cap\emptyset", @"\left\{ 3,4\right\} \cap \emptyset ", @"\emptyset ")] + [InlineData(@"\{3,4\}\cap\{4,5\}", @"\left\{ 3,4\right\} \cap \left\{ 4,5\right\} ", @"\left\{ 4\right\} ")] + [InlineData(@"\{2,3,4\}\setminus\{4\}", @"\left\{ 2,3,4\right\} \setminus \left\{ 4\right\} ", @"\left\{ 2,3\right\} ")] + [InlineData(@"\emptyset^\complement", @"\mathbb{C}\setminus \emptyset ", @"\mathbb{C}")] + [InlineData(@"\{3\}^\complement", @"\mathbb{C}\setminus \left\{ 3\right\} ", @"\mathbb{C}\setminus \left\{ 3\right\} ")] + [InlineData(@"\mathbb R+1", @"\mathbb{R}+1", @"\mathbb{R}+1")] + [InlineData(@"\sin^\complement x", @"\mathbb{C}\setminus \sin \left( x\right) ", @"\mathbb{C}\setminus \sin \left( x\right) ")] + [InlineData(@"1^\complement", @"\mathbb{C}\setminus 1", @"\mathbb{C}\setminus 1")] + public void SetOperations(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"(1,2)", @"\left( 1,2\right) ", @"\left( 1,2\right) ")] + [InlineData(@"[1,2)", @"\left[ 1,2\right) ", @"\left[ 1,2\right) ")] + [InlineData(@"(1,2]", @"\left( 1,2\right] ", @"\left( 1,2\right] ")] + [InlineData(@"[1,2]", @"\left[ 1,2\right] ", @"\left[ 1,2\right] ")] + [InlineData(@"(2,1)", @"\left( 2,1\right) ", @"\left( 2,1\right) ")] + [InlineData(@"[2,1)", @"\left[ 2,1\right) ", @"\left[ 2,1\right) ")] + [InlineData(@"(2,1]", @"\left( 2,1\right] ", @"\left( 2,1\right] ")] + [InlineData(@"[2,1]", @"\left[ 2,1\right] ", @"\left[ 2,1\right] ")] + [InlineData(@"[1,2]\setminus\{1\}", @"\left[ 1,2\right] \setminus \left\{ 1\right\} ", @"\left[ 1,2\right] \setminus \left\{ 1\right\} ")] + [InlineData(@"[1,2]\setminus\{2\}", @"\left[ 1,2\right] \setminus \left\{ 2\right\} ", @"\left[ 1,2\right] \setminus \left\{ 2\right\} ")] + [InlineData(@"[1,2]\cup(2,3)", @"\left[ 1,2\right] \cup \left( 2,3\right) ", @"\left[ 1,3\right) ")] + [InlineData(@"[1,2]\cap[1.5,1.6]", @"\left[ 1,2\right] \cap \left[ \frac{3}{2},\frac{8}{5}\right] ", @"\left[ \frac{3}{2},\frac{8}{5}\right] ")] + [InlineData(@"[1.5,1.5]", @"\left\{ \frac{3}{2}\right\} ", @"\left\{ \frac{3}{2}\right\} ")] + [InlineData(@"(1.5,1.5]", @"\emptyset ", @"\emptyset ")] + [InlineData(@"[1.5,1.5)", @"\emptyset ", @"\emptyset ")] + [InlineData(@"(1.5,1.5)", @"\emptyset ", @"\emptyset ")] + [InlineData(@"[xy,xy]", @"\left\{ xy\right\} ", @"\left\{ xy\right\} ")] + [InlineData(@"(xy,xy]", @"\emptyset ", @"\emptyset ")] + [InlineData(@"[xy,xy)", @"\emptyset ", @"\emptyset ")] + [InlineData(@"(xy,xy)", @"\emptyset ", @"\emptyset ")] + [InlineData(@"\frac{[7, 8]}{9}", @"\frac{\left[ 7,8\right] }{9}", @"\frac{\left[ 7,8\right] }{9}")] + [InlineData(@"+(3,4]", @"\left( 3,4\right] ", @"\left( 3,4\right] ")] + public void Intervals(string latex, string converted, string result) { + Test(latex, converted, result); + Test(latex.Replace(",", ";"), converted, result); + } + [Theory] + [InlineData(@"\>\:\pi", @"\mathrm{\pi }", @"\mathrm{\pi }")] + [InlineData(@"3\quad 2", @"3\cdot 2", "6")] + [InlineData(@"a\textstyle b\displaystyle", @"ab", @"ab")] + [InlineData(@"a^6+2a^6 % should be 3a^6", @"a^6+2a^6", @"3a^6")] + [InlineData(@"4+\ \mkern1.5mu3", @"4+3", "7")] + public void SkipInvisible(string latex, string converted, string output) => Test(latex, converted, output); + [Theory] + [InlineData(@"", "There is nothing to evaluate")] + [InlineData(@"\ ", "There is nothing to evaluate")] + [InlineData(@"\;", "There is nothing to evaluate")] + [InlineData(@"\quad", "There is nothing to evaluate")] + [InlineData(@"+", "Missing right operand for +")] + [InlineData(@"-", "Missing right operand for \u2212")] + [InlineData(@"\times", "Unsupported Unary Operator ×")] + [InlineData(@"\div", "Unsupported Unary Operator ÷")] + [InlineData(@"\%", "Missing left operand for %")] + [InlineData(@"\degree", "Missing left operand for °")] + [InlineData(@"\dagger", "Unsupported Unary Operator †")] + [InlineData(@"\times x", "Unsupported Unary Operator ×")] + [InlineData(@"\div x", "Unsupported Unary Operator ÷")] + [InlineData(@"\% x", "Missing left operand for %")] + [InlineData(@"\degree x", "Missing left operand for °")] + [InlineData(@"x+", "Missing right operand for +")] + [InlineData(@"x-", "Missing right operand for \u2212")] + [InlineData(@"x\times", "Missing right operand for ×")] + [InlineData(@"x\div", "Missing right operand for ÷")] + [InlineData(@"x\dagger", "Unsupported Binary Operator †")] + [InlineData(@"+^2", "Superscripts are unsupported for Unary Operator +")] + [InlineData(@"+_2", "Missing right operand for +")] + [InlineData(@"+^21", "Superscripts are unsupported for Unary Operator +")] + [InlineData(@"+_21", "Subscripts are unsupported for Unary Operator +")] + [InlineData(@"1+^21", "Superscripts are unsupported for Binary Operator +")] + [InlineData(@"1+_21", "Subscripts are unsupported for Binary Operator +")] + [InlineData(@"-_31", "Subscripts are unsupported for Unary Operator −")] + [InlineData(@"1\times_41", "Subscripts are unsupported for Binary Operator ×")] + [InlineData(@"\div_51", "Unsupported Unary Operator ÷")] + [InlineData(@"1\%_6", "Subscripts are unsupported for Ordinary %")] + [InlineData(@"1\degree_7", "Subscripts are unsupported for Ordinary °")] + [InlineData(@"\dagger_8", "Unsupported Unary Operator †")] + [InlineData(@".", "Invalid number: .")] + [InlineData(@"..", "Invalid number: ..")] + [InlineData(@"1..", "Invalid number: 1..")] + [InlineData(@"..1", "Invalid number: ..1")] + [InlineData(@"a_+", "Unsupported Unary Operator + in subscript")] + [InlineData(@"a_|", "Unsupported Ordinary | in subscript")] + [InlineData(@"a_{1+1}", "Unsupported Binary Operator + in subscript")] + [InlineData(@"a_{2^3}", "Unsupported exponentiation in subscript")] + [InlineData(@"a_{a2^3}", "Unsupported exponentiation in subscript")] + [InlineData(@"a_{a^32}", "Unsupported exponentiation in subscript")] + [InlineData(@"a_{2_3}", "Unsupported subscript in subscript")] + [InlineData(@"a_{a2_3}", "Unsupported subscript in subscript")] + [InlineData(@"a_{a_32}", "Unsupported subscript in subscript")] + [InlineData(@"a_{1,2}", "Unsupported Punctuation , in subscript")] + [InlineData(@"\square", "Placeholders should be filled")] + [InlineData(@"x^\square", "Placeholders should be filled")] + [InlineData(@"\square^x", "Placeholders should be filled")] + [InlineData(@"a_\square", "Placeholders should be filled")] + [InlineData(@"\square_a", "Placeholders should be filled")] + [InlineData(@"(", "Missing )")] + [InlineData(@"(_21)", "Subscripts are unsupported for Open (")] + [InlineData(@"(x", "Missing )")] + [InlineData(@"((x)", "Missing )")] + [InlineData(@"(+", "Missing right operand for +")] + [InlineData(@")", "Missing (")] + [InlineData(@"(1)_2", "Subscripts are unsupported for Close )")] + [InlineData(@"x)", "Missing (")] + [InlineData(@"(x))", "Missing (")] + [InlineData(@"+)", "Missing right operand for +")] + [InlineData(@"\left(\right)", "Missing math inside ( )")] + [InlineData(@"\left(1+\right)", "Missing right operand for +")] + [InlineData(@"\left(2,3\right)^\square", "Placeholders should be filled")] + [InlineData(@"(2,3)^\square", "Placeholders should be filled")] + [InlineData(@"[", "Missing ]")] + [InlineData(@"[_21)", "Unrecognized bracket pair [ )")] + [InlineData(@"[x", "Missing ]")] + [InlineData(@"[x)", "Unrecognized bracket pair [ )")] + [InlineData(@"[[x)", "Unrecognized bracket pair [ )")] + [InlineData(@"[+", "Missing right operand for +")] + [InlineData(@"[x))", "Unrecognized bracket pair [ )")] + [InlineData(@"\left[\right)", "Unrecognized bracket pair [ )")] + [InlineData(@"\left[1+\right)", "Missing right operand for +")] + [InlineData(@"\left[2,3\right)^\square", "Placeholders should be filled")] + [InlineData(@"[2,3)^\square", "Placeholders should be filled")] + [InlineData(@"((x]", "Unrecognized bracket pair ( ]")] + [InlineData(@"(x]", "Unrecognized bracket pair ( ]")] + [InlineData(@"]", "Missing [")] + [InlineData(@"]_2", "Subscripts are unsupported for Close ]")] + [InlineData(@"x]", "Missing [")] + [InlineData(@"(x]]", "Unrecognized bracket pair ( ]")] + [InlineData(@"+]", "Missing right operand for +")] + [InlineData(@"\left(\right]", "Unrecognized bracket pair ( ]")] + [InlineData(@"\left(1+\right]", "Missing right operand for +")] + [InlineData(@"\left(2,3\right]^\square", "Placeholders should be filled")] + [InlineData(@"(2,3]^\square", "Placeholders should be filled")] + [InlineData(@"[]", "Missing math inside [ ]")] + [InlineData(@"[[x]", "Missing ]")] + [InlineData(@"[x]]", "Missing [")] + [InlineData(@"[_\square x]", "Subscripts are unsupported for Open [")] + [InlineData(@"[x]_\square", "Subscripts are unsupported for Close ]")] + [InlineData(@"\left[\right]", "Missing math inside [ ]")] + [InlineData(@"\left[1+\right]", "Missing right operand for +")] + [InlineData(@"\left[2,3\right]^\square", "Placeholders should be filled")] + [InlineData(@"[2,3]^\square", "Placeholders should be filled")] + [InlineData(@"\{", "Missing }")] + [InlineData(@"\{_2\}", "Subscripts are unsupported for Open {")] + [InlineData(@"\{x", "Missing }")] + [InlineData(@"\{\{x\}", "Missing }")] + [InlineData(@"\{+", "Missing right operand for +")] + [InlineData(@"\}", "Missing {")] + [InlineData(@"\}_2", "Subscripts are unsupported for Close }")] + [InlineData(@"x\}", "Missing {")] + [InlineData(@"\{x\}\}", "Missing {")] + [InlineData(@"+\}", "Missing right operand for +")] + [InlineData(@"\left\{1+\right\}", "Missing right operand for +")] + [InlineData(@"\left\{\{\right\}\}", @"Missing }")] + [InlineData(@"\{2,3\}^\square", "Placeholders should be filled")] + [InlineData(@"(^2)", "Superscripts are unsupported for Open Bracket (")] + [InlineData(@"[^2]", "Superscripts are unsupported for Open Bracket [")] + [InlineData(@"\{^2\}", "Superscripts are unsupported for Open Bracket {")] + [InlineData(@"\left\{2,3\right\}^\square", "Placeholders should be filled")] + [InlineData(@"\frac{}{x}", "Missing numerator")] + [InlineData(@"\frac{x}{}", "Missing denominator")] + [InlineData(@"\sqrt{}", "Missing radicand")] + [InlineData(@"\sin", "Missing argument for sin")] + [InlineData(@"\cos-", "Missing right operand for \u2212")] + [InlineData(@"\tan\times", "Unsupported Unary Operator ×")] + [InlineData(@"\cot^(-1)", "Missing )")] + [InlineData(@"\sec\csc", "Missing argument for csc")] + [InlineData(@"\arcsin_2x", "Subscripts are unsupported for Large Operator arcsin")] + [InlineData(@"\operatorname{dab}", "Unsupported Large Operator dab")] + [InlineData(@"\begin{matrix}\end{matrix}", "Missing matrix element")] + [InlineData(@"\begin{matrix}2&3\end{matrix}_2", "Subscripts are unsupported for Ordinary")] + [InlineData(@"\begin{matrix}2&3\end{matrix}^2", "Superscripts are unsupported for Ordinary")] + [InlineData(@"\begin{pmatrix}2&+\end{pmatrix}", "Missing right operand for +")] + [InlineData(@"\begin{pmatrix}2&3\end{pmatrix}_2", "Subscripts are unsupported for Inner ")] + [InlineData(@"\begin{Vmatrix}\end{Vmatrix}", "Missing matrix element")] + [InlineData(@"\begin{Vmatrix}2&3\end{Vmatrix}", "Unrecognized bracket pair ‖ ‖")] + [InlineData(@"\begin{eqnarray}&&\end{eqnarray}", "Unsupported table environment eqnarray")] + [InlineData(@",", "Missing left operand for comma")] + [InlineData(@"1,_22", "Subscripts are unsupported for Punctuation ,")] + [InlineData(@"1,", "Missing right operand for comma")] + [InlineData(@",1", "Missing left operand for comma")] + [InlineData(@",1,2", "Missing left operand for comma")] + [InlineData(@"1,,2", "Missing left operand for comma")] + [InlineData(@"1,2,", "Missing right operand for comma")] + [InlineData(@",,1,2", "Missing left operand for comma")] + [InlineData(@"1,,2,", "Missing left operand for comma")] + [InlineData(@"1,2,,", "Missing left operand for comma")] + [InlineData(@"\arcsin(1,2)", "Comma cannot be argument for arcsin")] + [InlineData(@"[5,6)\times", "Missing right operand for ×")] + [InlineData(@"\sqrt[{[)}]{}", "Unrecognized bracket pair [ )")] + [InlineData(@"\sqrt[{[a,b]}]{}", "Missing radicand")] + [InlineData(@"\cap", "Unsupported Unary Operator ∩")] + [InlineData(@"\cap1", "Unsupported Unary Operator ∩")] + [InlineData(@"1\cap", "Missing right operand for ∩")] + [InlineData(@"\cup", "Unsupported Unary Operator ∪")] + [InlineData(@"\cup1", "Unsupported Unary Operator ∪")] + [InlineData(@"1\cup", "Missing right operand for ∪")] + [InlineData(@"\setminus", "Unsupported Unary Operator ∖")] + [InlineData(@"\setminus1", "Unsupported Unary Operator ∖")] + [InlineData(@"1\setminus", "Missing right operand for ∖")] + [InlineData(@"^\complement", "Superscripts are unsupported for Ordinary")] + [InlineData(@"_\complement", "Subscripts are unsupported for Ordinary")] + [InlineData(@"1_\complement", "Subscripts are unsupported for Number 1")] + [InlineData(@"x_\complement", "Unsupported Ordinary ∁ in subscript")] + [InlineData(@"\sin_\complement x", "Subscripts are unsupported for Large Operator sin")] + [InlineData(@",\cap", "Missing left operand for comma")] + [InlineData(@";\cap", "Missing left operand for comma")] + [InlineData(@"1,\cap", "Unsupported Unary Operator ∩")] + [InlineData(@"1;\cap", "Unsupported Unary Operator ∩")] + [InlineData(@"\cap,", "Unsupported Unary Operator ∩")] + [InlineData(@"\cap;", "Unsupported Unary Operator ∩")] + [InlineData(@"\left|1,2\right|", "Comma cannot be abs argument")] + [InlineData(@"1^+", "+ alone in superscript but not in limit subscript context")] + [InlineData(@"1^-", "\u2212 alone in superscript but not in limit subscript context")] + [InlineData(@"\lim", "Missing limit variable in subscript")] + [InlineData(@"\lim_x", "Missing → in limit subscript")] + [InlineData(@"\lim_{xy}", "Missing → in limit subscript")] + [InlineData(@"\lim_{xy^+}", "Missing → in limit subscript")] + [InlineData(@"\lim_{xy^-}", "Missing → in limit subscript")] + [InlineData(@"\lim_{\to}", "Missing limit variable in subscript")] + [InlineData(@"\lim_{\to2}", "Missing limit variable in subscript")] + [InlineData(@"\lim_{x\to}", "Missing limit target in subscript")] + [InlineData(@"\lim_{xy\to}", "Missing limit target in subscript")] + [InlineData(@"\lim_{xy\to^+}", "Missing limit target in subscript")] + [InlineData(@"\lim_{xy\to^-}", "Missing limit target in subscript")] + [InlineData(@"\lim_{x\to2^+2}", "Limit direction indicator + not placed at the end")] + [InlineData(@"\lim_{xy\to2^+2}", "Limit direction indicator + not placed at the end")] + [InlineData(@"\lim_{xy\to2^-2}", "Limit direction indicator \u2212 not placed at the end")] + [InlineData(@"\lim_{xy\to(2^+)}", "+ alone in superscript but not in limit subscript context")] + [InlineData(@"\lim_{xy\to(2^-)}", "\u2212 alone in superscript but not in limit subscript context")] + [InlineData(@"\lim_{xy\to\left(2^+\right)}", "+ alone in superscript but not in limit subscript context")] + [InlineData(@"\lim_{xy\to\left(2^-\right)}", "\u2212 alone in superscript but not in limit subscript context")] + [InlineData(@"\lim_{x,y}", "Comma cannot be limit variable in subscript")] + [InlineData(@"\lim_{x\to2}", "Missing right operand for lim")] + [InlineData(@"(\lim_{x\to2})x", "Missing right operand for lim")] + [InlineData(@"\lim_{x\to2}\cdot 1", "Unsupported Unary Operator ·")] + [InlineData(@"\lim_{x\to2}^6x", "Superscripts are unsupported for Large Operator lim")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}}", "Missing derivative variable")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}", "Missing right operand for derivative operator")] + [InlineData(@"\frac{\mathrm{d}^{1.5}}{\mathrm{d}x^{1.5}}", "Derivative order must be an integer, got 1.5")] + [InlineData(@"\frac{\mathrm{d}^a}{\mathrm{d}x^a}", "Derivative order must be an integer")] + [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}}", "Missing derivative variable")] + [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}^2}", "The d in derivative denominator cannot have an exponent. Did you mean to write it at the end of the denominator?")] + [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}^2}", "The d in derivative denominator cannot have an exponent. Did you mean to write it at the end of the denominator?")] + [InlineData(@"\frac{\mathrm{d}^3}{\mathrm{d}x^2}", "Derivative order mismatch: 3 in numerator but 2 is in denominator")] + [InlineData(@"\frac{\mathrm{d}^0}{\mathrm{d}x^1}", "Derivative order mismatch: 0 in numerator but 1 is in denominator")] + [InlineData(@"\frac{\mathrm{d}^{-1}}{\mathrm{d}x^1}", "Derivative order mismatch: -1 in numerator but 1 is in denominator")] + [InlineData(@"\frac{\mathrm{d}^{-3}}{\mathrm{d}x^{-2}}", "Derivative order mismatch: -3 in numerator but -2 is in denominator")] + [InlineData(@"\frac{\mathrm{d}^{-3}}{\mathrm{d}x^{-2.5}}", "Derivative order must be an integer, got -2.5")] + [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}(x^2)}", "Derivative order mismatch: 2 in numerator requires 2 in denominator")] + [InlineData(@"\frac{\mathrm{d}^2\cdot 1}{\mathrm{d}x^2}", "Unsupported Unary Operator ·")] + [InlineData(@"\left(\frac{\mathrm{d}^2}{\mathrm{d}x^2}\right)x", "Missing right operand for derivative operator")] + [InlineData(@"\int", "Missing integral variable. Did you forget to prepend it with upright d?")] + [InlineData(@"\int_0", "Integrals with only the lower limit are not supported")] + [InlineData(@"\int^0", "Integrals with only the upper limit are not supported")] + [InlineData(@"\int0", "Missing integral variable. Did you forget to prepend it with upright d?")] + [InlineData(@"\int_0^0", "Missing integral variable. Did you forget to prepend it with upright d?")] + [InlineData(@"\mathrm{d}", "d alone but not in integral body context")] + [InlineData(@"\int(\mathrm{d}", "d alone but not in integral body context")] + [InlineData(@"\int1,2\mathrm{d}x", "d alone but not in integral body context")] + [InlineData(@"\mathrm{for}", "Unsupported Unary Operator for")] + [InlineData(@"1\mathrm{for}", "Missing right operand for for")] + [InlineData(@"\mathrm{for}2", "Unsupported Unary Operator for")] + [InlineData(@"\mathrm{for},", "Unsupported Unary Operator for")] + [InlineData(@"1\mathrm{for},", "Missing right operand for for")] + [InlineData(@"1\mathrm{for}^23,", "for operator cannot have a superscript")] + [InlineData(@"1\mathrm{for}_23,", "for operator cannot have a subscript")] + [InlineData(@"\begin{cases}1,2\end{cases}", "Comma cannot be case expression")] + [InlineData(@"\begin{cases}1&3,4\end{cases}", "Comma cannot be case predicate")] + [InlineData(@"\begin{cases}1,2&3,4\end{cases}", "Comma cannot be case expression")] + [InlineData(@"\begin{cases}&3,4\end{cases}", "Missing case expression")] + [InlineData(@"\begin{cases}1&\end{cases}", "Missing case predicate")] + [InlineData(@"\begin{cases}1&\mathrm{for}^23\end{cases}", "for operator cannot have a superscript")] + [InlineData(@"\begin{cases}1&\mathrm{for}_23\end{cases}", "for operator cannot have a subscript")] + [InlineData(@"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}&ERROR\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", "The cases environment must have 1 to 2 columns per row")] + public void Error(string badLaTeX, string error) => + Evaluation.Evaluate(ParseLaTeX(badLaTeX)) + .Match(entity => throw new Xunit.Sdk.XunitException(entity.Latexise()), e => Assert.Equal(error, e)); + // https://github.com/ForNeVeR/wpf-math/issues/42 + [Theory] + [InlineData("1 + 1 - 1", @"1+1-1")] + [InlineData("3 / 2", @"\frac{3}{2}")] + [InlineData("2 * 3 / 5", @"\frac{2\cdot 3}{5}")] + [InlineData("sin(x+pi)", @"\sin \left( x+\mathrm{\pi }\right) ")] + [InlineData("sin(aaa)", @"\sin \left( \mathrm{aaa}\right) ")] + [InlineData("sin(aaa aaa aa)", @"\sin \left( \mathrm{aaa}\cdot \mathrm{aaa}\cdot \mathrm{aa}\right) ")] + [InlineData("log(3,alpha)", @"\log _3\left( \alpha \right) ")] + [InlineData("a_2k", @"a_{\mathrm{2k}}")] + [InlineData("2e9", @"2000000000")] + [InlineData("-3e4^x", @"-30000^x")] + [InlineData("(-3e4)^x", @"\left( -30000\right) ^x")] + [InlineData("i+2i", @"\mathrm{i}+2\mathrm{i}")] + public void SimpleArithmeticSyntax(string simpleSyntax, string latex) => + Assert.Equal(latex, LaTeXParser.MathListToLaTeX(Evaluation.Visualize((Entity)simpleSyntax)).ToString()); + [Theory] + [InlineData(@"\top", @"\top ", @"\top ")] + [InlineData(@"\bot", @"\bot ", @"\bot ")] + [InlineData(@"\neg\top", @"\neg \top ", @"\bot ")] + [InlineData(@"\neg\bot", @"\neg \bot ", @"\top ")] + [InlineData(@"\top\land\top", @"\top \wedge \top ", @"\top ")] + [InlineData(@"\top\land\bot", @"\top \wedge \bot ", @"\bot ")] + [InlineData(@"\bot\land\top", @"\bot \wedge \top ", @"\bot ")] + [InlineData(@"\bot\land\bot", @"\bot \wedge \bot ", @"\bot ")] + [InlineData(@"\top\lor\top", @"\top \vee \top ", @"\top ")] + [InlineData(@"\top\lor\bot", @"\top \vee \bot ", @"\top ")] + [InlineData(@"\bot\lor\top", @"\bot \vee \top ", @"\top ")] + [InlineData(@"\bot\lor\bot", @"\bot \vee \bot ", @"\bot ")] + [InlineData(@"\top\veebar\top", @"\top \veebar \top ", @"\bot ")] + [InlineData(@"\top\veebar\bot", @"\top \veebar \bot ", @"\top ")] + [InlineData(@"\bot\veebar\top", @"\bot \veebar \top ", @"\top ")] + [InlineData(@"\bot\veebar\bot", @"\bot \veebar \bot ", @"\bot ")] + [InlineData(@"\top\barwedge\top", @"\neg \left( \top \wedge \top \right) ", @"\bot ")] + [InlineData(@"\top\barwedge\bot", @"\neg \left( \top \wedge \bot \right) ", @"\top ")] + [InlineData(@"\bot\barwedge\top", @"\neg \left( \bot \wedge \top \right) ", @"\top ")] + [InlineData(@"\bot\barwedge\bot", @"\neg \left( \bot \wedge \bot \right) ", @"\top ")] + [InlineData(@"\top\rightarrow\top", @"\top \rightarrow \top ", @"\top ")] + [InlineData(@"\top\rightarrow\bot", @"\top \rightarrow \bot ", @"\bot ")] + [InlineData(@"\bot\rightarrow\top", @"\bot \rightarrow \top ", @"\top ")] + [InlineData(@"\bot\rightarrow\bot", @"\bot \rightarrow \bot ", @"\top ")] + [InlineData(@"\top\nrightarrow\top", @"\neg \left( \top \rightarrow \top \right) ", @"\bot ")] + [InlineData(@"\top\nrightarrow\bot", @"\neg \left( \top \rightarrow \bot \right) ", @"\top ")] + [InlineData(@"\bot\nrightarrow\top", @"\neg \left( \bot \rightarrow \top \right) ", @"\bot ")] + [InlineData(@"\bot\nrightarrow\bot", @"\neg \left( \bot \rightarrow \bot \right) ", @"\bot ")] + [InlineData(@"\top\leftarrow\top", @"\top \rightarrow \top ", @"\top ")] + [InlineData(@"\bot\leftarrow\top", @"\top \rightarrow \bot ", @"\bot ")] + [InlineData(@"\top\leftarrow\bot", @"\bot \rightarrow \top ", @"\top ")] + [InlineData(@"\bot\leftarrow\bot", @"\bot \rightarrow \bot ", @"\top ")] + [InlineData(@"\top\nleftarrow\top", @"\neg \left( \top \rightarrow \top \right) ", @"\bot ")] + [InlineData(@"\bot\nleftarrow\top", @"\neg \left( \top \rightarrow \bot \right) ", @"\top ")] + [InlineData(@"\top\nleftarrow\bot", @"\neg \left( \bot \rightarrow \top \right) ", @"\bot ")] + [InlineData(@"\bot\nleftarrow\bot", @"\neg \left( \bot \rightarrow \bot \right) ", @"\bot ")] + [InlineData(@"\top\leftrightarrow\top", @"\neg \left( \top \veebar \top \right) ", @"\top ")] + [InlineData(@"\bot\leftrightarrow\top", @"\neg \left( \bot \veebar \top \right) ", @"\bot ")] + [InlineData(@"\top\leftrightarrow\bot", @"\neg \left( \top \veebar \bot \right) ", @"\bot ")] + [InlineData(@"\bot\leftrightarrow\bot", @"\neg \left( \bot \veebar \bot \right) ", @"\top ")] + [InlineData(@"\top\nleftrightarrow\top", @"\top \veebar \top ", @"\bot ")] + [InlineData(@"\bot\nleftrightarrow\top", @"\bot \veebar \top ", @"\top ")] + [InlineData(@"\top\nleftrightarrow\bot", @"\top \veebar \bot ", @"\top ")] + [InlineData(@"\bot\nleftrightarrow\bot", @"\bot \veebar \bot ", @"\bot ")] + [InlineData(@"x=x", @"x=x", @"\top ")] + [InlineData(@"x\le x", @"x\leq x", @"\top ")] + [InlineData(@"x\leq x", @"x\leq x", @"\top ")] + [InlineData(@"x\leqslant x", @"x\leq x", @"\top ")] + [InlineData(@"x\neq y", @"x\neq y", @"x\neq y")] // Cannot simplify without knowing x and y + [InlineData(@"1<2", @"1<2", @"\top ")] + [InlineData(@"2<1", @"2<1", @"\bot ")] + [InlineData(@"1\le1", @"1\leq 1", @"\top ")] + [InlineData(@"2>1", @"2>1", @"\top ")] + [InlineData(@"1>2", @"1>2", @"\bot ")] + [InlineData(@"1\ge1", @"1\geq 1", @"\top ")] + [InlineData(@"1\geq1", @"1\geq 1", @"\top ")] + [InlineData(@"1\geqslant1", @"1\geq 1", @"\top ")] + [InlineData(@"x\in\{x,y\}", @"x\in \left\{ x,y\right\} ", @"\top ")] + [InlineData(@"z\notin\{x,y\}", @"\neg z\in \left\{ x,y\right\} ", @"\neg z\in \left\{ x,y\right\} ")] // Cannot determine this without knowing x, y, and z + [InlineData(@"\{x,y\}\ni x", @"x\in \left\{ x,y\right\} ", @"\top ")] + public void LogicalAndRelationalOperators(string latex, string converted, string result) => Test(latex, converted, result); + + [Theory] + // Test that ∧ binds tighter than ∨ + [InlineData(@"\bot\land\bot\lor\top", @"\bot \wedge \bot \vee \top ", @"\top ")] + [InlineData(@"\top\lor\bot\land\bot", @"\top \vee \bot \wedge \bot ", @"\top ")] + // Test that ∧ binds tighter than ⊻ + [InlineData(@"\top\veebar\bot\land\top", @"\top \veebar \bot \wedge \top ", @"\top ")] + [InlineData(@"\top\land\bot\veebar\top", @"\top \wedge \bot \veebar \top ", @"\top ")] + // Test that ⊻ binds tighter than ∨ + [InlineData(@"\top\veebar \bot\lor \top", @"\top \veebar \bot \vee \top ", @"\top ")] + [InlineData(@"\top\vee \bot\veebar \top", @"\top \vee \bot \veebar \top ", @"\top ")] + // Test that → is right associative and lowest precedence + [InlineData(@"a\to b\lor c", @"a\rightarrow b\vee c", @"a\rightarrow b\vee c")] + [InlineData(@"a\lor b\to c", @"a\vee b\rightarrow c", @"a\vee b\rightarrow c")] + [InlineData(@"a\to b\to c", @"a\rightarrow b\rightarrow c", @"a\rightarrow b\rightarrow c")] + [InlineData(@"a\to (b\to c)", @"a\rightarrow b\rightarrow c", @"a\rightarrow b\rightarrow c")] + [InlineData(@"(a\to b)\to c", @"\left( a\rightarrow b\right) \rightarrow c", @"\left( a\rightarrow b\right) \rightarrow c")] + // Complex expression + [InlineData(@"a\to b\land c\lor d", @"a\rightarrow b\wedge c\vee d", @"a\rightarrow b\wedge c\vee d")] + // Test ¬ has highest precedence + [InlineData(@"\neg \top\land\top", @"\neg \top \wedge \top ", @"\bot ")] + [InlineData(@"\neg a\lor\neg a", @"\neg a\vee \neg a", @"\neg a")] + // Test relational operators bind tighter than logical + [InlineData(@"a=b\land c=d", @"a=b\wedge c=d", @"a=b\wedge c=d")] + [InlineData(@"ad", @"ad", @"ad")] + public void LogicalOperatorPrecedence(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"13")] + [InlineData(@"1<(2<3)", @"1<\left( 2<3\right) ", @"1<\top ", @"\bot ", @"1>\bot ")] + [InlineData(@"1<2<3", @"1<2<3", @"\top ", @"\bot ", @"\bot ")] + [InlineData(@"3<2<3", @"3<2<3", @"\bot ")] + [InlineData(@"1<2<1", @"1<2<1", @"\bot ")] + [InlineData(@"1<1<1", @"1<1<1", @"\bot ", @"\top ")] + [InlineData(@"1\leq 2<3", @"1\leq 2<3", @"\top ", @"\bot ", @"\bot ")] + [InlineData(@"3\leq 2<3", @"3\leq 2<3", @"\bot ")] + [InlineData(@"1\leq 2<1", @"1\leq 2<1", @"\bot ")] + [InlineData(@"1\leq 1<1", @"1\leq 1<1", @"\bot ", @"\top ", @"\bot ")] + [InlineData(@"1\leq 1=1", @"1\leq 1=1", @"\top ")] + [InlineData(@"1\leq 1\geq1", @"1\leq 1\geq 1", @"\top ")] + public void ChainedComparisons(string latex, string converted, string result, string? eqResult = null, string? geqResult = null) { + Test(latex, converted, result); + eqResult ??= result.Replace("<", "=").Replace(@"\leq ", "="); + Test(latex.Replace("<", "=").Replace(@"\leq ", "="), + converted.Replace("<", "=").Replace(@"\leq ", "="), + eqResult); + Test(latex.Replace("<", ">").Replace(@"\leq ", @"\geq "), + converted.Replace("<", ">").Replace(@"\leq ", @"\geq "), + geqResult ?? result.Replace("<", ">").Replace(@"\leq ", @"\geq ")); + Test(latex.Replace("<", @"\neq ").Replace(@"\leq ", @"\neq "), + converted.Replace("<", @"\neq ").Replace(@"\leq ", @"\neq "), + eqResult.Replace("=", @"\neq ").Replace(@"\top ", @"|||").Replace(@"\bot ", @"\top ").Replace(@"|||", @"\bot ")); + } + [Theory] + [InlineData(@"\mathrm{undefined}", @"\mathrm{undefined}", @"\mathrm{undefined}")] + [InlineData(@"0\times\infty", @"0\cdot \infty ", @"\mathrm{undefined}")] + [InlineData(@"\mathrm{undefined}+1", @"\mathrm{undefined}+1", @"\mathrm{undefined}")] + [InlineData(@"\sin\mathrm{undefined}", @"\sin \left( \mathrm{undefined}\right) ", @"\mathrm{undefined}")] + public void Undefined(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\left|1\right|", @"\left| 1\right| ", @"1")] + [InlineData(@"-\left|-1\right|", @"-\left| -1\right| ", @"-1")] + [InlineData(@"-\operatorname{abs}(-1)", @"-\left| -1\right| ", @"-1")] + [InlineData(@"-\operatorname{abs}\left|-1\right|", @"-\left| \left| -1\right| \right| ", @"-1")] + [InlineData(@"-\left|1\right|^2", @"-\left| 1\right| ^2", @"-1")] + [InlineData(@"\operatorname{sgn}\operatorname{abs} x", @"\operatorname{sgn} \left( \left| x\right| \right) ", @"1")] + public void Abs(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\lim_{x\to2}x+1", @"\lim _{x\rightarrow 2}x+1", @"3")] + [InlineData(@"\lim_{x\to2^-}x+1", @"\lim _{x\rightarrow 2^-}x+1", @"3")] + [InlineData(@"\lim_{x\to2^+}x+1", @"\lim _{x\rightarrow 2^+}x+1", @"3")] + [InlineData(@"\lim_{x\to\infty}x", @"\lim _{x\rightarrow \infty }x", @"\infty ")] + [InlineData(@"\lim_{x\to0}\frac1x+1", @"\lim _{x\rightarrow 0}\frac{1}{x}+1", @"\mathrm{undefined}")] + [InlineData(@"\lim_{x\to0^-}\frac1x+1", @"\lim _{x\rightarrow 0^-}\frac{1}{x}+1", @"-\infty ")] + [InlineData(@"\lim_{x\to0^+}\frac1x+1", @"\lim _{x\rightarrow 0^+}\frac{1}{x}+1", @"\infty ")] + [InlineData(@"\lim_{x\to\infty}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty }\left( \frac{1}{x}\right) ^2x^2+1", @"2")] + [InlineData(@"\lim_{x\to\infty^-}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty ^-}\left( \frac{1}{x}\right) ^2x^2+1", @"2")] + [InlineData(@"\lim_{x\to\infty^+}\frac1x^2x^2+1", @"\lim _{x\rightarrow \infty ^+}\left( \frac{1}{x}\right) ^2x^2+1", @"2")] + [InlineData(@"\lim_{x\to1+x}x", @"\lim _{x\rightarrow 1+x}x", @"\infty ")] + [InlineData(@"\lim_{x\to(1+x)}x", @"\lim _{x\rightarrow 1+x}x", @"\infty ")] + [InlineData(@"\lim_{x\to(1+x)^+}x", @"\lim _{x\rightarrow \left( 1+x\right) ^+}x", @"\infty ")] + [InlineData(@"\lim_{x\to(1+x)^-}x", @"\lim _{x\rightarrow \left( 1+x\right) ^-}x", @"\infty ")] + [InlineData(@"\lim_{x\to(y)}(x)y+y", @"\lim _{x\rightarrow y}xy+y", @"y^2+y")] + [InlineData(@"\lim_{x\to(y)^+}(x)y+y", @"\lim _{x\rightarrow y^+}xy+y", @"y^2+y")] + [InlineData(@"\lim_{x\to(y)^-}(x)y+y", @"\lim _{x\rightarrow y^-}xy+y", @"y^2+y")] + [InlineData(@"\lim_{y\to zzz_2}x", @"\lim _{y\rightarrow zzz_2}x", @"x")] + [InlineData(@"\lim_{y\to zzz_2^-}x", @"\lim _{y\rightarrow \left( zzz_2\right) ^-}x", @"x")] + [InlineData(@"\lim_{y\to zzz_2^+}x", @"\lim _{y\rightarrow \left( zzz_2\right) ^+}x", @"x")] + [InlineData(@"\lim_{xyy\to zzz_2}x", @"\lim _{xyy\rightarrow zzz_2}x", @"\lim _{xy^2\rightarrow z^2\cdot z_2}x")] + [InlineData(@"\lim_{xyy\to zzz_2^-}x", @"\lim _{xyy\rightarrow \left( zzz_2\right) ^-}x", @"\lim _{xy^2\rightarrow \left( z^2\cdot z_2\right) ^-}x")] + [InlineData(@"\lim_{xyy\to zzz_2^+}x", @"\lim _{xyy\rightarrow \left( zzz_2\right) ^+}x", @"\lim _{xy^2\rightarrow \left( z^2\cdot z_2\right) ^+}x")] + [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz}}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz}}x", @"x")] + [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz}^-}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz^{\mathnormal{-}}}}x", @"x")] + [InlineData(@"\lim_{\mathrm{xy}\to\mathrm{zz^+}}x", @"\lim _{\mathrm{xy}\rightarrow \mathrm{zz^{\mathnormal{+}}}}x", @"x")] + [InlineData(@"(\lim_{x\to y}(z)^2)^3", @"\left( \lim _{x\rightarrow y}z^2\right) ^3", @"z^6")] + public void Limit(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}x", @"\frac{\mathrm{d}}{\mathrm{d}x}x", @"1")] + [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x}", @"\frac{\mathrm{d}}{\mathrm{d}x}x", @"1")] + [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x}x", @"\left( \frac{\mathrm{d}}{\mathrm{d}x}x\right) \cdot x", @"x")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"1+y")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}(x)}(x)y+1", @"\frac{\mathrm{d}}{\mathrm{d}x}xy+1", @"1+y")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x^2}x^2y+1", @"\frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x^2\cdot y+1", @"1+y")] + [InlineData(@"\frac{\mathrm{d}x^2}{\mathrm{d}x^2}y+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x^2\right) \cdot y+1", @"1+y")] + [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}x^2}xy+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x\right) \cdot xy+1", @"1")] + [InlineData(@"\frac{\mathrm{d}x}{\mathrm{d}(x^2)}(x)y+1", @"\left( \frac{\mathrm{d}}{\mathrm{d}\left( x^2\right) }x\right) \cdot xy+1", @"1")] + [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}x^2}x^2y+1", @"\frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}x^2}x^2\cdot y+1", @"1+2y")] + [InlineData(@"\frac{\mathrm{d}^2}{\mathrm{d}(x^2)^2}x^2y+1", @"\frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}\left( x^2\right) ^2}x^2\cdot y+1", @"1")] + [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}x^2}x^2y+1", @"\left( \frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}x^2}x\right) \cdot x^2\cdot y+1", @"1")] + [InlineData(@"\frac{\mathrm{d}^2x}{\mathrm{d}(x^2)^2}x^2y+1", @"\left( \frac{\mathrm{d^{\mathnormal{2}}}}{\mathrm{d}\left( x^2\right) ^2}x\right) \cdot x^2\cdot y+1", @"1")] + [InlineData(@"\frac{\mathrm{d}}{\mathrm{d}x}\lim_{y\to z}xy+x", @"\frac{\mathrm{d}}{\mathrm{d}x}\lim _{y\rightarrow z}xy+x", @"x+z")] + [InlineData(@"\frac{\mathrm{d}^0x}{\mathrm{d}x^0}y+1", @"\left( \frac{\mathrm{d^{\mathnormal{0}}}}{\mathrm{d}x^0}x\right) \cdot y+1", @"1+xy")] + [InlineData(@"\frac{\mathrm{d}^{-1}x}{\mathrm{d}x^{-1}}y+1", @"\left( \int x\, \mathrm{d}x\right) \cdot y+1", @"\left( \frac{x^2}{2}+C\right) \cdot y+1")] + [InlineData(@"\frac\mathrm{dy}\mathrm{dx}\mathrm{dx}", @"\frac{\mathrm{dy}}{\mathrm{dx}}\cdot \mathrm{dx}", @"\mathrm{dy}\quad \mathrm{for}\quad \mathrm{dx}\neq 0")] // upright letters are interpreted together. + [InlineData(@"\frac\mathrm{d\ y}\mathrm{d\ x}", @"\frac{\mathrm{d}}{\mathrm{d}x}y", @"0")] // spaces are required to separate upright letters. + [InlineData(@"\frac\mathrm{dx}\mathrm{dx}", @"\frac{\mathrm{dx}}{\mathrm{dx}}", @"1\quad \mathrm{for}\quad \mathrm{dx}\neq 0")] + public void Derivative(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\int\mathrm{d}x", @"\int 1\, \mathrm{d}x", @"C+x")] + [InlineData(@"\int\int\mathrm{d}x\mathrm{d}y", @"\int \int 1\, \mathrm{d}x\, \mathrm{d}y", @"C_1+\left( C+x\right) \cdot y")] + [InlineData(@"(\int\int\mathrm{d}x\mathrm{d}y)", @"\int \int 1\, \mathrm{d}x\, \mathrm{d}y", @"C_1+\left( C+x\right) \cdot y")] + [InlineData(@"\int\int C\mathrm{d}x\mathrm{d}y", @"\int \int C\, \mathrm{d}x\, \mathrm{d}y", @"\left( C_1+Cx\right) \cdot y+C_2")] + [InlineData(@"\int\frac\mathrm{d}{\mathrm{d}x}\int C\mathrm{d}x\mathrm{d}y", @"\int \frac{\mathrm{d}}{\mathrm{d}x}\int C\, \mathrm{d}x\, \mathrm{d}y", @"C_1+Cy")] + [InlineData(@"\int x\,\mathrm{d}x", @"\int x\, \mathrm{d}x", @"\frac{x^2}{2}+C")] + [InlineData(@"\int x+y\,\mathrm{d}x", @"\int \left( x+y\right) \, \mathrm{d}x", @"\frac{\left( x+y\right) ^2}{2}+C")] + [InlineData(@"2\int x+y\,\mathrm{d}x\cdot 3", @"2\left( \int \left( x+y\right) \, \mathrm{d}x\right) \cdot 3", @"6\left( C+\frac{\left( x+y\right) ^2}{2}\right) ")] + [InlineData(@"2+\int\sin x+y\,\mathrm{d}\sin x+3", @"2+\int \left( \sin \left( x\right) +y\right) \, \mathrm{d}\left( \sin \left( x\right) \right) +3", @"5+\frac{\left( \sin \left( x\right) +y\right) ^2}{2}+C")] + [InlineData(@"2+\int xy+z\,\mathrm{d}xy\cdot 3", @"2+\left( \int \left( xy+z\right) \, \mathrm{d}\left( xy\right) \right) \cdot 3", @"2+\left( \frac{\left( xy+z\right) ^2}{2}+C\right) \cdot 3")] + [InlineData(@"\int_0^1\mathrm{d}x", @"\int _0^11\, \mathrm{d}x", @"1")] + [InlineData(@"\int_{10}^{11}\mathrm{d}x", @"\int _{10}^{11}1\, \mathrm{d}x", @"1")] + [InlineData(@"\int_{10}^{11}x\mathrm{d}x", @"\int _{10}^{11}x\, \mathrm{d}x", @"\frac{21}{2}")] + [InlineData(@"\int_{-\infty}^\infty\frac1{x^2+1}\mathrm{d}x", @"\int _{-\infty }^{\infty }\frac{1}{x^2+1}\, \mathrm{d}x", @"\mathrm{\pi }")] + public void Integral(string latex, string converted, string result) => Test(latex, converted, result); + [Theory] + [InlineData(@"x\mathrm{for}y", @"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y")] + [InlineData(@"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y", @"x\quad \mathrm{for}\quad y")] + [InlineData(@"(x\mathrm{for}y)\mathrm{for}z", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] + [InlineData(@"x\mathrm{for}(y\mathrm{for}z)", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] + [InlineData(@"x\mathrm{for}y\mathrm{for}z", @"x\quad \mathrm{for}\quad y\quad \mathrm{for}\quad z", @"x\quad \mathrm{for}\quad y\wedge z")] + [InlineData(@"x\mathrm{for}y,y\mathrm{for}z,z\mathrm{for}a\mathrm{for}b", @"x\quad \mathrm{for}\quad y,y\quad \mathrm{for}\quad z,z\quad \mathrm{for}\quad a\quad \mathrm{for}\quad b", null)] + [InlineData(@"x=2=y\mathrm{for}x\leftrightarrow y", @"x=2=y\quad \mathrm{for}\quad \neg \left( x\veebar y\right) ", @"x=2=y\quad \mathrm{for}\quad \neg \left( x\veebar y\right) ")] + public void Provided(string latex, string converted, string? result) => Test(latex, converted, result); + [Theory] + [InlineData(@"\begin{cases}x+1\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\end{array}\right. ", + @"1+x")] + [InlineData(@"\begin{cases}x+1\\x-1\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", + @"1+x")] + [InlineData(@"\begin{cases}x+1&\mathrm{otherwise}\\x-1\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", + @"1+x")] + [InlineData(@"\begin{cases}x+1&\top\\x-1&\mathrm{otherwise}\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", + @"1+x")] + [InlineData(@"\begin{cases}x+1&\mathrm{otherwise}\\x-1&\mathrm{for}\top\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{otherwise}\\ \textstyle x-1&\textstyle \mathrm{otherwise}\end{array}\right. ", + @"1+x")] + [InlineData(@"\begin{cases}x+1 & x<0\\x-1 & x\geq0\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ")] + [InlineData(@"\begin{cases}x+1 & \mathrm{for}\ x<0\\x-1 & \mathrm{for}\ x\geq0\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }x<0\\ \textstyle x-1&\textstyle \mathrm{for\ }x\geq 0\end{array}\right. ")] + [InlineData(@"\begin{cases}x+1 & \mathrm{for} x<0\mathrm{for} y\\x-1 & \mathrm{for}\ x\geq0\mathrm{for} y\end{cases}", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }\left( x<0\quad \mathrm{for}\quad y\right) \\ \textstyle x-1&\textstyle \mathrm{for\ }\left( x\geq 0\quad \mathrm{for}\quad y\right) \end{array}\right. ", + @"\left\{ \, \begin{array}{ll}\textstyle x+1&\textstyle \mathrm{for\ }\left( x<0\quad \mathrm{for}\quad y\right) \\ \textstyle x-1&\textstyle \mathrm{for\ }\left( x\geq 0\quad \mathrm{for}\quad y\right) \end{array}\right. ")] + public void Piecewise(string latex, string converted, string result) => Test(latex, converted, result); + } } \ No newline at end of file diff --git a/CSharpMath.Rendering.Tests/TestRenderingSharedData.cs b/CSharpMath.Rendering.Tests/TestRenderingSharedData.cs index e9fcff56..0ba0f08e 100644 --- a/CSharpMath.Rendering.Tests/TestRenderingSharedData.cs +++ b/CSharpMath.Rendering.Tests/TestRenderingSharedData.cs @@ -1,10 +1,11 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace CSharpMath.Rendering.Tests { - public abstract class TestRenderingSharedData : IEnumerable where TThis : TestRenderingSharedData { + public abstract class TestRenderingSharedData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TThis> : IEnumerable where TThis : TestRenderingSharedData { public static IReadOnlyDictionary AllConstants { get; } = typeof(TThis) .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) diff --git a/CSharpMath.Xaml.Tests.NuGet/CSharpMath.Xaml.Tests.NuGet.csproj b/CSharpMath.Xaml.Tests.NuGet/CSharpMath.Xaml.Tests.NuGet.csproj index 9a98af27..968e49e1 100644 --- a/CSharpMath.Xaml.Tests.NuGet/CSharpMath.Xaml.Tests.NuGet.csproj +++ b/CSharpMath.Xaml.Tests.NuGet/CSharpMath.Xaml.Tests.NuGet.csproj @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/CSharpMath.Xaml.Tests.NuGet/Test.cs b/CSharpMath.Xaml.Tests.NuGet/Test.cs index 04d80d4b..f31c400e 100644 --- a/CSharpMath.Xaml.Tests.NuGet/Test.cs +++ b/CSharpMath.Xaml.Tests.NuGet/Test.cs @@ -1,26 +1,26 @@ -namespace CSharpMath.Xaml.Tests.NuGet { - using Avalonia; - public class Program { - static string File(string platform, [System.Runtime.CompilerServices.CallerFilePath] string thisDir = "") => - System.IO.Path.Combine(thisDir, "..", $"Test.{platform}.png"); - [Xunit.Fact] - public void TestImage() { - // TODO: old versions of SkiaSharp fail to initialize on arm64 macOS since they only have x64 native libraries - //global::Avalonia.Skia.SkiaPlatform.Initialize(); - - // TODO: Update to use MAUI package - //using (var forms = System.IO.File.OpenWrite(File(nameof(Forms)))) - // new Forms.MathView { LaTeX = "1", FontSize = 50 }.Painter.DrawAsStream()?.CopyTo(forms); - // TODO: Fix Avalonia package issue complaining about missing Avalonia.Visual reference. - //using (var avalonia = System.IO.File.OpenWrite(File(nameof(Avalonia)))) - // new Avalonia.MathView { LaTeX = "1", FontSize = 50 }.Painter.DrawAsPng(avalonia); - - var expected = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform - (System.Runtime.InteropServices.OSPlatform.Windows) ? 344 : 292; - //using (var forms = System.IO.File.OpenRead(File(nameof(Forms)))) - // Xunit.Assert.Equal(expected, forms.Length); - //using (var avalonia = System.IO.File.OpenRead(File(nameof(Avalonia)))) - // Xunit.Assert.Equal(expected, avalonia.Length); - } - } +namespace CSharpMath.Xaml.Tests.NuGet { + using Avalonia; + public class Program { + static string File(string platform, [System.Runtime.CompilerServices.CallerFilePath] string thisDir = "") => + System.IO.Path.Combine(thisDir, "..", $"Test.{platform}.png"); + [Xunit.Fact] + public void TestImage() { + // TODO: old versions of SkiaSharp fail to initialize on arm64 macOS since they only have x64 native libraries + //global::Avalonia.Skia.SkiaPlatform.Initialize(); + + // TODO: Update to use MAUI package + //using (var forms = System.IO.File.OpenWrite(File(nameof(Forms)))) + // new Forms.MathView { LaTeX = "1", FontSize = 50 }.Painter.DrawAsStream()?.CopyTo(forms); + // TODO: Fix Avalonia package issue complaining about missing Avalonia.Visual reference. + //using (var avalonia = System.IO.File.OpenWrite(File(nameof(Avalonia)))) + // new Avalonia.MathView { LaTeX = "1", FontSize = 50 }.Painter.DrawAsPng(avalonia); + + var expected = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform + (System.Runtime.InteropServices.OSPlatform.Windows) ? 344 : 292; + //using (var forms = System.IO.File.OpenRead(File(nameof(Forms)))) + // Xunit.Assert.Equal(expected, forms.Length); + //using (var avalonia = System.IO.File.OpenRead(File(nameof(Avalonia)))) + // Xunit.Assert.Equal(expected, avalonia.Length); + } + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Accent.cs b/CSharpMath/Atom/Atoms/Accent.cs index 95735aac..1643012a 100644 --- a/CSharpMath/Atom/Atoms/Accent.cs +++ b/CSharpMath/Atom/Atoms/Accent.cs @@ -1,25 +1,25 @@ -using System.Text; - -namespace CSharpMath.Atom.Atoms { - /// An accented atom - public sealed class Accent : MathAtom, IMathListContainer { - public MathList InnerList { get; } = new MathList(); - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public Accent(string value, MathList? innerList = null) : base(value) => - InnerList = innerList ?? new MathList(); - public override string DebugString => - new StringBuilder(@"\accent") - .AppendInBracesOrLiteralNull(Nucleus) - .AppendInBracesOrLiteralNull(InnerList?.DebugString) - .ToString(); - public new Accent Clone(bool finalize) => (Accent)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new Accent(Nucleus, InnerList.Clone(finalize)); - public override bool ScriptsAllowed => true; - public bool EqualsAccent(Accent other) => - EqualsAtom(other) && InnerList.NullCheckingStructuralEquality(other?.InnerList); - public override bool Equals(object obj) => obj is Accent a ? EqualsAccent(a) : false; - public override int GetHashCode() => (base.GetHashCode(), InnerList).GetHashCode(); - } +using System.Text; + +namespace CSharpMath.Atom.Atoms { + /// An accented atom + public sealed class Accent : MathAtom, IMathListContainer { + public MathList InnerList { get; } = new MathList(); + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public Accent(string value, MathList? innerList = null) : base(value) => + InnerList = innerList ?? new MathList(); + public override string DebugString => + new StringBuilder(@"\accent") + .AppendInBracesOrLiteralNull(Nucleus) + .AppendInBracesOrLiteralNull(InnerList?.DebugString) + .ToString(); + public new Accent Clone(bool finalize) => (Accent)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new Accent(Nucleus, InnerList.Clone(finalize)); + public override bool ScriptsAllowed => true; + public bool EqualsAccent(Accent other) => + EqualsAtom(other) && InnerList.NullCheckingStructuralEquality(other?.InnerList); + public override bool Equals(object obj) => obj is Accent a ? EqualsAccent(a) : false; + public override int GetHashCode() => (base.GetHashCode(), InnerList).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/ColorBox.cs b/CSharpMath/Atom/Atoms/ColorBox.cs index 6ea29839..6b26c307 100644 --- a/CSharpMath/Atom/Atoms/ColorBox.cs +++ b/CSharpMath/Atom/Atoms/ColorBox.cs @@ -1,22 +1,22 @@ -using System.Drawing; -using System.Text; - -namespace CSharpMath.Atom.Atoms { - public sealed class ColorBox : MathAtom, IMathListContainer { - public Color Color { get; set; } - public MathList InnerList { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public ColorBox(Color color, MathList innerList) : base(string.Empty) => - (Color, InnerList) = (color, innerList); - public override string DebugString => - new StringBuilder(@"\colorbox") - .AppendInBracesOrLiteralNull(Color.ToString()) - .AppendInBracesOrLiteralNull(InnerList.DebugString).ToString(); - public override bool ScriptsAllowed => false; - public new ColorBox Clone(bool finalize) => (ColorBox)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new ColorBox(Color, InnerList.Clone(finalize)); - public override int GetHashCode() => (base.GetHashCode(), Color, InnerList).GetHashCode(); - } +using System.Drawing; +using System.Text; + +namespace CSharpMath.Atom.Atoms { + public sealed class ColorBox : MathAtom, IMathListContainer { + public Color Color { get; set; } + public MathList InnerList { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public ColorBox(Color color, MathList innerList) : base(string.Empty) => + (Color, InnerList) = (color, innerList); + public override string DebugString => + new StringBuilder(@"\colorbox") + .AppendInBracesOrLiteralNull(Color.ToString()) + .AppendInBracesOrLiteralNull(InnerList.DebugString).ToString(); + public override bool ScriptsAllowed => false; + public new ColorBox Clone(bool finalize) => (ColorBox)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new ColorBox(Color, InnerList.Clone(finalize)); + public override int GetHashCode() => (base.GetHashCode(), Color, InnerList).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Fraction.cs b/CSharpMath/Atom/Atoms/Fraction.cs index 6c637645..e1083a37 100644 --- a/CSharpMath/Atom/Atoms/Fraction.cs +++ b/CSharpMath/Atom/Atoms/Fraction.cs @@ -1,39 +1,39 @@ -using System.Text; - -namespace CSharpMath.Atom.Atoms { - public sealed class Fraction : MathAtom, IMathListContainer { - public MathList Numerator { get; } - public MathList Denominator { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { Numerator, Denominator }; - public Boundary LeftDelimiter { get; set; } - public Boundary RightDelimiter { get; set; } - /// In this context, a "rule" is a fraction line. - public bool HasRule { get; } - public Fraction(MathList numerator, MathList denominator, bool hasRule = true) => - (Numerator, Denominator, HasRule) = (numerator, denominator, hasRule); - public override bool ScriptsAllowed => true; - public new Fraction Clone(bool finalize) => (Fraction)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => +using System.Text; + +namespace CSharpMath.Atom.Atoms { + public sealed class Fraction : MathAtom, IMathListContainer { + public MathList Numerator { get; } + public MathList Denominator { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { Numerator, Denominator }; + public Boundary LeftDelimiter { get; set; } + public Boundary RightDelimiter { get; set; } + /// In this context, a "rule" is a fraction line. + public bool HasRule { get; } + public Fraction(MathList numerator, MathList denominator, bool hasRule = true) => + (Numerator, Denominator, HasRule) = (numerator, denominator, hasRule); + public override bool ScriptsAllowed => true; + public new Fraction Clone(bool finalize) => (Fraction)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => new Fraction(Numerator.Clone(finalize), Denominator.Clone(finalize), HasRule) { LeftDelimiter = LeftDelimiter, RightDelimiter = RightDelimiter - }; - public override string DebugString => - new StringBuilder(HasRule ? @"\frac" : @"\atop") - .AppendInBracketsOrNothing(LeftDelimiter.Nucleus) - .AppendInBracketsOrNothing(RightDelimiter.Nucleus) - .AppendInBracesOrEmptyBraces(Numerator?.DebugString) - .AppendInBracesOrEmptyBraces(Denominator?.DebugString) - .AppendDebugStringOfScripts(this).ToString(); - public override bool Equals(object obj) => obj is Fraction f && EqualsFraction(f); - public bool EqualsFraction(Fraction other) => - EqualsAtom(other) - && Numerator.NullCheckingStructuralEquality(other.Numerator) - && Denominator.NullCheckingStructuralEquality(other.Denominator) - && LeftDelimiter == other.LeftDelimiter - && RightDelimiter == other.RightDelimiter; - public override int GetHashCode() => - (base.GetHashCode(), Numerator, Denominator, LeftDelimiter, RightDelimiter).GetHashCode(); - } + }; + public override string DebugString => + new StringBuilder(HasRule ? @"\frac" : @"\atop") + .AppendInBracketsOrNothing(LeftDelimiter.Nucleus) + .AppendInBracketsOrNothing(RightDelimiter.Nucleus) + .AppendInBracesOrEmptyBraces(Numerator?.DebugString) + .AppendInBracesOrEmptyBraces(Denominator?.DebugString) + .AppendDebugStringOfScripts(this).ToString(); + public override bool Equals(object obj) => obj is Fraction f && EqualsFraction(f); + public bool EqualsFraction(Fraction other) => + EqualsAtom(other) + && Numerator.NullCheckingStructuralEquality(other.Numerator) + && Denominator.NullCheckingStructuralEquality(other.Denominator) + && LeftDelimiter == other.LeftDelimiter + && RightDelimiter == other.RightDelimiter; + public override int GetHashCode() => + (base.GetHashCode(), Numerator, Denominator, LeftDelimiter, RightDelimiter).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Inner.cs b/CSharpMath/Atom/Atoms/Inner.cs index 05026ebd..6772fc3c 100644 --- a/CSharpMath/Atom/Atoms/Inner.cs +++ b/CSharpMath/Atom/Atoms/Inner.cs @@ -1,32 +1,32 @@ -using System.Text; - -namespace CSharpMath.Atom.Atoms { - /// An inner atom, i.e. embedded math list - public sealed class Inner : MathAtom, IMathListContainer { - public Inner(Boundary left, MathList innerList, Boundary right) => - (LeftBoundary, InnerList, RightBoundary) = (left, innerList, right); - public MathList InnerList { get; } - public Boundary LeftBoundary { get; } - public Boundary RightBoundary { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public override bool ScriptsAllowed => true; - public new Inner Clone(bool finalize) => (Inner)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new Inner(LeftBoundary, InnerList.Clone(finalize), RightBoundary); - public bool EqualsInner(Inner otherInner) => - EqualsAtom(otherInner) - && InnerList.NullCheckingStructuralEquality(otherInner.InnerList) - && LeftBoundary.NullCheckingStructuralEquality(otherInner.LeftBoundary) - && RightBoundary.NullCheckingStructuralEquality(otherInner.RightBoundary); - public override bool Equals(object obj) => obj is Inner i ? EqualsInner(i) : false; - public override int GetHashCode() => - (base.GetHashCode(), InnerList, LeftBoundary, RightBoundary).GetHashCode(); - public override string DebugString => - new StringBuilder(@"\inner") - .AppendInBracesOrEmptyBraces(LeftBoundary.Nucleus) - .AppendInBracesOrLiteralNull(InnerList.DebugString) - .AppendInBracesOrEmptyBraces(RightBoundary.Nucleus) - .AppendDebugStringOfScripts(this).ToString(); - } +using System.Text; + +namespace CSharpMath.Atom.Atoms { + /// An inner atom, i.e. embedded math list + public sealed class Inner : MathAtom, IMathListContainer { + public Inner(Boundary left, MathList innerList, Boundary right) => + (LeftBoundary, InnerList, RightBoundary) = (left, innerList, right); + public MathList InnerList { get; } + public Boundary LeftBoundary { get; } + public Boundary RightBoundary { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public override bool ScriptsAllowed => true; + public new Inner Clone(bool finalize) => (Inner)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new Inner(LeftBoundary, InnerList.Clone(finalize), RightBoundary); + public bool EqualsInner(Inner otherInner) => + EqualsAtom(otherInner) + && InnerList.NullCheckingStructuralEquality(otherInner.InnerList) + && LeftBoundary.NullCheckingStructuralEquality(otherInner.LeftBoundary) + && RightBoundary.NullCheckingStructuralEquality(otherInner.RightBoundary); + public override bool Equals(object obj) => obj is Inner i ? EqualsInner(i) : false; + public override int GetHashCode() => + (base.GetHashCode(), InnerList, LeftBoundary, RightBoundary).GetHashCode(); + public override string DebugString => + new StringBuilder(@"\inner") + .AppendInBracesOrEmptyBraces(LeftBoundary.Nucleus) + .AppendInBracesOrLiteralNull(InnerList.DebugString) + .AppendInBracesOrEmptyBraces(RightBoundary.Nucleus) + .AppendDebugStringOfScripts(this).ToString(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/LargeOperator.cs b/CSharpMath/Atom/Atoms/LargeOperator.cs index a22785f2..8ddaa9e0 100644 --- a/CSharpMath/Atom/Atoms/LargeOperator.cs +++ b/CSharpMath/Atom/Atoms/LargeOperator.cs @@ -1,29 +1,29 @@ namespace CSharpMath.Atom.Atoms { - /// AMSMath class 1: Prefix operator, e.g. \sin, \cos, \sum, \prod, \int - public sealed class LargeOperator : MathAtom { - bool? _limits; - /// - /// True: \limits - /// False: \nolimits - /// Null: (unset, depends on line style) - /// - public bool? Limits { get => ForceNoLimits ? false : _limits; set => _limits = value; } - ///If true, overrides Limits and makes it treated as false - public bool ForceNoLimits { get; } - public LargeOperator(string value, bool? limits, bool forceNoLimits = false) : base(value) { - Limits = limits; - ForceNoLimits = forceNoLimits; - } - public new LargeOperator Clone(bool finalize) => (LargeOperator)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new LargeOperator(Nucleus, Limits, ForceNoLimits); - public override bool ScriptsAllowed => true; - public override string DebugString => base.DebugString + Limits switch { - true => @"\limits", - false when !ForceNoLimits => @"\nolimits", - _ => "" - }; - // Don't care about \limits or \nolimits - public bool EqualsLargeOperator(LargeOperator op) => EqualsAtom(op); - } + /// AMSMath class 1: Prefix operator, e.g. \sin, \cos, \sum, \prod, \int + public sealed class LargeOperator : MathAtom { + bool? _limits; + /// + /// True: \limits + /// False: \nolimits + /// Null: (unset, depends on line style) + /// + public bool? Limits { get => ForceNoLimits ? false : _limits; set => _limits = value; } + ///If true, overrides Limits and makes it treated as false + public bool ForceNoLimits { get; } + public LargeOperator(string value, bool? limits, bool forceNoLimits = false) : base(value) { + Limits = limits; + ForceNoLimits = forceNoLimits; + } + public new LargeOperator Clone(bool finalize) => (LargeOperator)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new LargeOperator(Nucleus, Limits, ForceNoLimits); + public override bool ScriptsAllowed => true; + public override string DebugString => base.DebugString + Limits switch { + true => @"\limits", + false when !ForceNoLimits => @"\nolimits", + _ => "" + }; + // Don't care about \limits or \nolimits + public bool EqualsLargeOperator(LargeOperator op) => EqualsAtom(op); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Overline.cs b/CSharpMath/Atom/Atoms/Overline.cs index de7c7f67..4e6a1d18 100644 --- a/CSharpMath/Atom/Atoms/Overline.cs +++ b/CSharpMath/Atom/Atoms/Overline.cs @@ -1,24 +1,24 @@ -using System.Text; - -namespace CSharpMath.Atom.Atoms { - /// An overlined atom - public sealed class Overline : MathAtom, IMathListContainer { - public Overline(MathList innerList) => InnerList = innerList; - public MathList InnerList { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public override bool ScriptsAllowed => true; - public new Overline Clone(bool finalize) => (Overline)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new Overline(InnerList.Clone(finalize)); - public override string DebugString => - new StringBuilder(@"\overline") - .AppendInBracesOrLiteralNull(InnerList?.DebugString) - .ToString(); - public bool EqualsOverline(Overline other) => - EqualsAtom(other) && InnerList.NullCheckingStructuralEquality(other?.InnerList); - public override bool Equals(object obj) => obj is Overline o ? EqualsOverline(o) : false; - public override int GetHashCode() => - (base.GetHashCode(), InnerList).GetHashCode(); - } +using System.Text; + +namespace CSharpMath.Atom.Atoms { + /// An overlined atom + public sealed class Overline : MathAtom, IMathListContainer { + public Overline(MathList innerList) => InnerList = innerList; + public MathList InnerList { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public override bool ScriptsAllowed => true; + public new Overline Clone(bool finalize) => (Overline)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new Overline(InnerList.Clone(finalize)); + public override string DebugString => + new StringBuilder(@"\overline") + .AppendInBracesOrLiteralNull(InnerList?.DebugString) + .ToString(); + public bool EqualsOverline(Overline other) => + EqualsAtom(other) && InnerList.NullCheckingStructuralEquality(other?.InnerList); + public override bool Equals(object obj) => obj is Overline o ? EqualsOverline(o) : false; + public override int GetHashCode() => + (base.GetHashCode(), InnerList).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Radical.cs b/CSharpMath/Atom/Atoms/Radical.cs index 03fbe526..5467b436 100644 --- a/CSharpMath/Atom/Atoms/Radical.cs +++ b/CSharpMath/Atom/Atoms/Radical.cs @@ -1,24 +1,24 @@ -using System.Text; - -namespace CSharpMath.Atom.Atoms { - public sealed class Radical : MathAtom, IMathListContainer { - public Radical(MathList degree, MathList radicand) => - (Degree, Radicand) = (degree, radicand); - public MathList Degree { get; } - /// Whatever is under the square root sign - public MathList Radicand { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { Degree, Radicand }; - public new Radical Clone(bool finalize) => (Radical)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new Radical(Degree.Clone(finalize), Radicand.Clone(finalize)); - public override bool ScriptsAllowed => true; - public override string DebugString => - new StringBuilder(@"\sqrt") - .AppendInBracketsOrNothing(Degree?.DebugString) - .AppendInBracesOrLiteralNull(Radicand?.DebugString) - .AppendDebugStringOfScripts(this).ToString(); - public override int GetHashCode() => - (base.GetHashCode(), Degree, Radicand).GetHashCode(); - } +using System.Text; + +namespace CSharpMath.Atom.Atoms { + public sealed class Radical : MathAtom, IMathListContainer { + public Radical(MathList degree, MathList radicand) => + (Degree, Radicand) = (degree, radicand); + public MathList Degree { get; } + /// Whatever is under the square root sign + public MathList Radicand { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { Degree, Radicand }; + public new Radical Clone(bool finalize) => (Radical)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new Radical(Degree.Clone(finalize), Radicand.Clone(finalize)); + public override bool ScriptsAllowed => true; + public override string DebugString => + new StringBuilder(@"\sqrt") + .AppendInBracketsOrNothing(Degree?.DebugString) + .AppendInBracesOrLiteralNull(Radicand?.DebugString) + .AppendDebugStringOfScripts(this).ToString(); + public override int GetHashCode() => + (base.GetHashCode(), Degree, Radicand).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/RaiseBox.cs b/CSharpMath/Atom/Atoms/RaiseBox.cs index 4a583bda..e8b71e3b 100644 --- a/CSharpMath/Atom/Atoms/RaiseBox.cs +++ b/CSharpMath/Atom/Atoms/RaiseBox.cs @@ -1,19 +1,19 @@ -namespace CSharpMath.Atom.Atoms { - public sealed class RaiseBox : MathAtom, IMathListContainer { - public RaiseBox(Atom.Length raise, MathList innerList) => - (Raise, InnerList) = (raise, innerList); - public Atom.Length Raise { get; } - public MathList InnerList { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public override bool ScriptsAllowed => false; - public new RaiseBox Clone(bool finalize) => (RaiseBox)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new RaiseBox(Raise, InnerList.Clone(finalize)); - public override int GetHashCode() => - (base.GetHashCode(), Raise, InnerList).GetHashCode(); - public override bool Equals(object obj) => - obj is RaiseBox r ? EqualsAtom(r) && Raise == r.Raise - && InnerList.NullCheckingStructuralEquality(r.InnerList) : false; - } +namespace CSharpMath.Atom.Atoms { + public sealed class RaiseBox : MathAtom, IMathListContainer { + public RaiseBox(Atom.Length raise, MathList innerList) => + (Raise, InnerList) = (raise, innerList); + public Atom.Length Raise { get; } + public MathList InnerList { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public override bool ScriptsAllowed => false; + public new RaiseBox Clone(bool finalize) => (RaiseBox)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new RaiseBox(Raise, InnerList.Clone(finalize)); + public override int GetHashCode() => + (base.GetHashCode(), Raise, InnerList).GetHashCode(); + public override bool Equals(object obj) => + obj is RaiseBox r ? EqualsAtom(r) && Raise == r.Raise + && InnerList.NullCheckingStructuralEquality(r.InnerList) : false; + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Space.cs b/CSharpMath/Atom/Atoms/Space.cs index a3e9b2b0..592fa4ba 100644 --- a/CSharpMath/Atom/Atoms/Space.cs +++ b/CSharpMath/Atom/Atoms/Space.cs @@ -1,20 +1,20 @@ -namespace CSharpMath.Atom.Atoms { - public sealed class Space : MathAtom { - private readonly Length _space; - public float Length => _space.Amount; - public bool IsMu => _space.IsMu; - public Space(Length space) : base() => - _space = space; - public override bool ScriptsAllowed => false; - public new Space Clone(bool finalize) => (Space)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => new Space(_space); - public float ActualLength - (Display.FrontEnd.FontMathTable mathTable, TFont font) - where TFont : Display.FrontEnd.IFont => _space.ActualLength(mathTable, font); - public override string DebugString => " "; - public override bool Equals(object obj) => obj is Space s && EqualsSpace(s); - public bool EqualsSpace(Space otherSpace) => - EqualsAtom(otherSpace) && Length == otherSpace.Length && IsMu == otherSpace.IsMu; - public override int GetHashCode() => (base.GetHashCode(), _space).GetHashCode(); - } +namespace CSharpMath.Atom.Atoms { + public sealed class Space : MathAtom { + private readonly Length _space; + public float Length => _space.Amount; + public bool IsMu => _space.IsMu; + public Space(Length space) : base() => + _space = space; + public override bool ScriptsAllowed => false; + public new Space Clone(bool finalize) => (Space)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => new Space(_space); + public float ActualLength + (Display.FrontEnd.FontMathTable mathTable, TFont font) + where TFont : Display.FrontEnd.IFont => _space.ActualLength(mathTable, font); + public override string DebugString => " "; + public override bool Equals(object obj) => obj is Space s && EqualsSpace(s); + public bool EqualsSpace(Space otherSpace) => + EqualsAtom(otherSpace) && Length == otherSpace.Length && IsMu == otherSpace.IsMu; + public override int GetHashCode() => (base.GetHashCode(), _space).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/Atoms/Underline.cs b/CSharpMath/Atom/Atoms/Underline.cs index 2996f38d..2f23b5f4 100644 --- a/CSharpMath/Atom/Atoms/Underline.cs +++ b/CSharpMath/Atom/Atoms/Underline.cs @@ -1,23 +1,23 @@ -namespace CSharpMath.Atom.Atoms { - /// An underlined atom - public sealed class Underline : MathAtom, IMathListContainer { - public Underline(MathList innerList) : base() => - InnerList = innerList; - public MathList InnerList { get; } - System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => - new[] { InnerList }; - public new Underline Clone(bool finalize) => (Underline)base.Clone(finalize); - protected override MathAtom CloneInside(bool finalize) => - new Underline(InnerList.Clone(finalize)); - public override bool ScriptsAllowed => true; - public override string DebugString => - new System.Text.StringBuilder(@"\underline") - .AppendInBracesOrLiteralNull(InnerList.DebugString) - .ToString(); - public bool EqualsUnderline(Underline other) => - EqualsAtom(other) && InnerList.EqualsList(other.InnerList); - public override bool Equals(object obj) => - obj is Underline u ? EqualsUnderline(u) : false; - public override int GetHashCode() => (base.GetHashCode(), InnerList).GetHashCode(); - } +namespace CSharpMath.Atom.Atoms { + /// An underlined atom + public sealed class Underline : MathAtom, IMathListContainer { + public Underline(MathList innerList) : base() => + InnerList = innerList; + public MathList InnerList { get; } + System.Collections.Generic.IEnumerable IMathListContainer.InnerLists => + new[] { InnerList }; + public new Underline Clone(bool finalize) => (Underline)base.Clone(finalize); + protected override MathAtom CloneInside(bool finalize) => + new Underline(InnerList.Clone(finalize)); + public override bool ScriptsAllowed => true; + public override string DebugString => + new System.Text.StringBuilder(@"\underline") + .AppendInBracesOrLiteralNull(InnerList.DebugString) + .ToString(); + public bool EqualsUnderline(Underline other) => + EqualsAtom(other) && InnerList.EqualsList(other.InnerList); + public override bool Equals(object obj) => + obj is Underline u ? EqualsUnderline(u) : false; + public override int GetHashCode() => (base.GetHashCode(), InnerList).GetHashCode(); + } } \ No newline at end of file diff --git a/CSharpMath/Atom/MathList.cs b/CSharpMath/Atom/MathList.cs index 2282e6fb..853672d7 100644 --- a/CSharpMath/Atom/MathList.cs +++ b/CSharpMath/Atom/MathList.cs @@ -1,129 +1,129 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace CSharpMath.Atom { - using Atoms; -#pragma warning disable CA1710 // Identifiers should have correct suffix - // WTF CA1710, you want types inheriting IList to have the Collection suffix? - class DisabledMathList : MathList { - internal DisabledMathList() { } - public override void Add(MathAtom item) => throw new InvalidOperationException("Scripts are not allowed!"); - public override void Append(IEnumerable list) => throw new InvalidOperationException("Scripts are not allowed!"); - } - public class MathList : IMathObject, IList, IReadOnlyList, IEquatable { -#pragma warning restore CA1710 // Identifiers should have correct suffix - public List Atoms { get; private set; } - public MathList() => Atoms = new List(); - public MathList(IEnumerable atoms) => Atoms = new List(atoms); - public MathList(params MathAtom[] atoms) => Atoms = new List(atoms); - - /// The last that is not a , - /// or when is empty. -#if !NETSTANDARD2_0 && !NET45 - [System.Diagnostics.CodeAnalysis.DisallowNull] -#endif - public MathAtom? Last { - get { - for (int i = Atoms.Count - 1; i >= 0; i--) - switch (Atoms[i]) { - case Comment _: - continue; - case var atom: - return atom; - } - return null; - } - set { - for (int i = Atoms.Count - 1; i >= 0; i--) - switch (Atoms[i]) { - case Comment _: - continue; - default: - Atoms[i] = value!; - return; - } - Atoms.Add(value!); - } - } - /// Just a deep copy if finalize is false; A finalized list if finalize is true - public MathList Clone(bool finalize) { - var newList = new MathList(); - if (!finalize) { - foreach (var atom in Atoms) - newList.Add(atom.Clone(finalize)); - } else { - foreach (var atom in Atoms) { - if (atom is Comment) { - var newComment = atom.Clone(finalize); - newComment.IndexRange = Range.NotFound; - newList.Add(newComment); - continue; - } - var prevNode = newList.Last; - var newNode = atom.Clone(finalize); - if (atom.IndexRange == Range.Zero) { - int prevIndex = - prevNode?.IndexRange.Location + prevNode?.IndexRange.Length ?? 0; - newNode.IndexRange = new Range(prevIndex, 1); - } - switch (prevNode, newNode) { - case (null or BinaryOperator or Relation or Open or Punctuation or LargeOperator, BinaryOperator b): - newNode = b.ToUnaryOperator(); - break; - case (BinaryOperator b, Relation or Punctuation or Close): - newList.Last = b.ToUnaryOperator(); - break; - case (Number n, Number _) when n.Superscript.IsEmpty() && n.Subscript.IsEmpty(): - n.Fuse(newNode); - continue; // do not add the new node; we fused it instead. - } - newList.Add(newNode); - } - } - return newList; - } - public int Count => Atoms.Count; - public string DebugString => - string.Concat(System.Linq.Enumerable.Select(Atoms, a => a.DebugString)); - public bool IsReadOnly => false; - public MathAtom this[int index] { get => Atoms[index]; set => Atoms[index] = value; } - public virtual void Append(IEnumerable list) => Atoms.AddRange(list); - public void RemoveAtoms(int index, int count) => Atoms.RemoveRange(index, count); - public bool EqualsList(MathList otherList) { - if (otherList == null) { - return false; - } - if (otherList.Count != Count) { - return false; - } - for (int i = 0; i < Count; i++) { - if (!this[i].NullCheckingStructuralEquality(otherList[i])) { - return false; - } - } - return true; - } - public override bool Equals(object obj) => obj is MathList l && EqualsList(l); - public override int GetHashCode() => - Atoms.Count == 0 ? 0 : Atoms.GetHashCode(); // Special case empty list for LaTeXDefaults - bool IEquatable.Equals(MathList otherList) => EqualsList(otherList); - public IEnumerator GetEnumerator() => Atoms.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => Atoms.GetEnumerator(); - public int IndexOf(MathAtom item) => Atoms.IndexOf(item); - public void Insert(int index, MathAtom item) { - if (item != null) Atoms.Insert(index, item); - else throw new ArgumentNullException(nameof(item), "MathList cannot contain null."); - } - public void RemoveAt(int index) => Atoms.RemoveAt(index); - public virtual void Add(MathAtom item) { - if (item != null) Atoms.Add(item); - else throw new ArgumentNullException(nameof(item), "MathList cannot contain null."); - } - public void Clear() => Atoms.Clear(); - public bool Contains(MathAtom item) => Atoms.Contains(item); - public void CopyTo(MathAtom[] array, int arrayIndex) => Atoms.CopyTo(array, arrayIndex); - public bool Remove(MathAtom item) => Atoms.Remove(item); - public MathList Slice(int index, int count) => new MathList { Atoms = Atoms.GetRange(index, count) }; - } +using System; +using System.Collections; +using System.Collections.Generic; + +namespace CSharpMath.Atom { + using Atoms; +#pragma warning disable CA1710 // Identifiers should have correct suffix + // WTF CA1710, you want types inheriting IList to have the Collection suffix? + class DisabledMathList : MathList { + internal DisabledMathList() { } + public override void Add(MathAtom item) => throw new InvalidOperationException("Scripts are not allowed!"); + public override void Append(IEnumerable list) => throw new InvalidOperationException("Scripts are not allowed!"); + } + public class MathList : IMathObject, IList, IReadOnlyList, IEquatable { +#pragma warning restore CA1710 // Identifiers should have correct suffix + public List Atoms { get; private set; } + public MathList() => Atoms = new List(); + public MathList(IEnumerable atoms) => Atoms = new List(atoms); + public MathList(params MathAtom[] atoms) => Atoms = new List(atoms); + + /// The last that is not a , + /// or when is empty. +#if !NETSTANDARD2_0 && !NET45 + [System.Diagnostics.CodeAnalysis.DisallowNull] +#endif + public MathAtom? Last { + get { + for (int i = Atoms.Count - 1; i >= 0; i--) + switch (Atoms[i]) { + case Comment _: + continue; + case var atom: + return atom; + } + return null; + } + set { + for (int i = Atoms.Count - 1; i >= 0; i--) + switch (Atoms[i]) { + case Comment _: + continue; + default: + Atoms[i] = value!; + return; + } + Atoms.Add(value!); + } + } + /// Just a deep copy if finalize is false; A finalized list if finalize is true + public MathList Clone(bool finalize) { + var newList = new MathList(); + if (!finalize) { + foreach (var atom in Atoms) + newList.Add(atom.Clone(finalize)); + } else { + foreach (var atom in Atoms) { + if (atom is Comment) { + var newComment = atom.Clone(finalize); + newComment.IndexRange = Range.NotFound; + newList.Add(newComment); + continue; + } + var prevNode = newList.Last; + var newNode = atom.Clone(finalize); + if (atom.IndexRange == Range.Zero) { + int prevIndex = + prevNode?.IndexRange.Location + prevNode?.IndexRange.Length ?? 0; + newNode.IndexRange = new Range(prevIndex, 1); + } + switch (prevNode, newNode) { + case (null or BinaryOperator or Relation or Open or Punctuation or LargeOperator, BinaryOperator b): + newNode = b.ToUnaryOperator(); + break; + case (BinaryOperator b, Relation or Punctuation or Close): + newList.Last = b.ToUnaryOperator(); + break; + case (Number n, Number _) when n.Superscript.IsEmpty() && n.Subscript.IsEmpty(): + n.Fuse(newNode); + continue; // do not add the new node; we fused it instead. + } + newList.Add(newNode); + } + } + return newList; + } + public int Count => Atoms.Count; + public string DebugString => + string.Concat(System.Linq.Enumerable.Select(Atoms, a => a.DebugString)); + public bool IsReadOnly => false; + public MathAtom this[int index] { get => Atoms[index]; set => Atoms[index] = value; } + public virtual void Append(IEnumerable list) => Atoms.AddRange(list); + public void RemoveAtoms(int index, int count) => Atoms.RemoveRange(index, count); + public bool EqualsList(MathList otherList) { + if (otherList == null) { + return false; + } + if (otherList.Count != Count) { + return false; + } + for (int i = 0; i < Count; i++) { + if (!this[i].NullCheckingStructuralEquality(otherList[i])) { + return false; + } + } + return true; + } + public override bool Equals(object obj) => obj is MathList l && EqualsList(l); + public override int GetHashCode() => + Atoms.Count == 0 ? 0 : Atoms.GetHashCode(); // Special case empty list for LaTeXDefaults + bool IEquatable.Equals(MathList otherList) => EqualsList(otherList); + public IEnumerator GetEnumerator() => Atoms.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Atoms.GetEnumerator(); + public int IndexOf(MathAtom item) => Atoms.IndexOf(item); + public void Insert(int index, MathAtom item) { + if (item != null) Atoms.Insert(index, item); + else throw new ArgumentNullException(nameof(item), "MathList cannot contain null."); + } + public void RemoveAt(int index) => Atoms.RemoveAt(index); + public virtual void Add(MathAtom item) { + if (item != null) Atoms.Add(item); + else throw new ArgumentNullException(nameof(item), "MathList cannot contain null."); + } + public void Clear() => Atoms.Clear(); + public bool Contains(MathAtom item) => Atoms.Contains(item); + public void CopyTo(MathAtom[] array, int arrayIndex) => Atoms.CopyTo(array, arrayIndex); + public bool Remove(MathAtom item) => Atoms.Remove(item); + public MathList Slice(int index, int count) => new MathList { Atoms = Atoms.GetRange(index, count) }; + } } \ No newline at end of file diff --git a/CSharpMath/CSharpMath.csproj b/CSharpMath/CSharpMath.csproj index 49031425..8d4a67f2 100644 --- a/CSharpMath/CSharpMath.csproj +++ b/CSharpMath/CSharpMath.csproj @@ -1,20 +1,20 @@ - - - netstandard2.0 - - The core of the C# math rendering engine - CSharpMath. - Can display beautiful math equations and symbols from the LaTeX format. - Needs a front end to function. - Currently, front ends exist for Avalonia, iOS, SkiaSharp and Xamarin.Forms. - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - + + + netstandard2.0 + + The core of the C# math rendering engine - CSharpMath. + Can display beautiful math equations and symbols from the LaTeX format. + Needs a front end to function. + Currently, front ends exist for Avalonia, iOS, SkiaSharp and Xamarin.Forms. + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + \ No newline at end of file