Skip to content

Commit

Permalink
Merge pull request #11 from DistractiveTeam/format-options
Browse files Browse the repository at this point in the history
Add format options
  • Loading branch information
tiakun committed Aug 12, 2023
2 parents 1014fdd + 6520484 commit bf05a24
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 48 deletions.
6 changes: 6 additions & 0 deletions Distractive.Formatters.Tests/ThaiBahtCompareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ IEnumerable<long> range() {
string s1 = GreatFriends.ThaiBahtText.ThaiBahtTextUtil.ThaiBahtText(i);
Assert.Equal(s1, s2);
});

Parallel.ForEach(range(), i => {
string s2 = i.ToThaiWords(true) + "บาทถ้วน";
string s1 = GreatFriends.ThaiBahtText.ThaiBahtTextUtil.ThaiBahtText(i, GreatFriends.ThaiBahtText.UsesEt.TensOnly);
Assert.Equal(s1, s2);
});
}
}
}
14 changes: 12 additions & 2 deletions Distractive.Formatters.Tests/ThaiNumberTextFormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ public class ThaiNumberTextFormatterTests
[InlineData(42, "สี่สิบสอง")]
[InlineData(100, "หนึ่งร้อย")]
[InlineData(101, "หนึ่งร้อยเอ็ด")]
[InlineData(101, "หนึ่งร้อยหนึ่ง", true)]
[InlineData(121, "หนึ่งร้อยยี่สิบเอ็ด")]
[InlineData(121, "หนึ่งร้อยยี่สิบเอ็ด", true)]
[InlineData(1001, "หนึ่งพันหนึ่ง", true)]
[InlineData(11_000_001, "สิบเอ็ดล้านเอ็ด")]
[InlineData(11_000_001, "สิบเอ็ดล้านหนึ่ง", true)]
[InlineData(101_000_001, "หนึ่งร้อยเอ็ดล้านเอ็ด")]
[InlineData(101_000_001, "หนึ่งร้อยหนึ่งล้านหนึ่ง", true)]
[InlineData(300, "สามร้อย")]
[InlineData(10_000_000, "สิบล้าน")]
[InlineData(9_223372_036854_775807, "เก้าล้านสองแสนสองหมื่นสามพันสามร้อยเจ็ดสิบสองล้านสามหมื่นหกพันแปดร้อยห้าสิบสี่ล้านเจ็ดแสนเจ็ดหมื่นห้าพันแปดร้อยเจ็ด")]
[InlineData(-300, "ลบสามร้อย")]
[InlineData(-1, "ลบหนึ่ง")]
public void FormatTest(long num, string formattedText)
public void FormatTest(long num, string formattedText, bool useEtWithTensOnly = false)
{
var formatter = new ThaiNumberTextFormatter();
var o = useEtWithTensOnly ? ThaiNumberTextFormatterOptions.EtWithTensOnly
: ThaiNumberTextFormatterOptions.Default;
var formatter = new ThaiNumberTextFormatter(o);
var text = formatter.Format(num);
Assert.Equal(formattedText, text);
}
Expand Down
6 changes: 6 additions & 0 deletions Distractive.Formatters/Distractive.Formatters.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@
<None Include="icons8-hiragana-ma-100.png" Pack="true" PackagePath="\" />
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
<PackageReference Include="IsExternalInit" Version="1.0.3" Condition="">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
32 changes: 28 additions & 4 deletions Distractive.Formatters/FormatterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,36 @@
namespace Distractive.Formatters
{
public static class FormatterExtensions
{
private static readonly ThaiNumberTextFormatter _formatter = new ThaiNumberTextFormatter();
{
private static readonly ThaiNumberTextFormatter[] _formatters
= new ThaiNumberTextFormatter[(int)ThaiNumberTextFormatFlags.TotalPerm]
{ new(), new(ThaiNumberTextFormatterOptions.EtWithTensOnly) };
private static ThaiNumberTextFormatter Get(ThaiNumberTextFormatterOptions options)
=> Get(options.FormatFlags);

public static string ToThaiWords(this long value) => _formatter.Format(value);
private static ThaiNumberTextFormatter Get(ThaiNumberTextFormatFlags flags) => _formatters[(int)flags];


public static string ToThaiWords(this long value) => _formatters[0].Format(value);
public static string ToThaiWords(this int value) => ToThaiWords((long)value);

public static string ToBahtText(this decimal value) => _formatter.GetBahtText(value);
public static string ToBahtText(this decimal value) => _formatters[0].GetBahtText(value);

public static string ToThaiWords(this long value,
ThaiNumberTextFormatterOptions options) => Get(options).Format(value);

public static string ToThaiWords(this int value,
ThaiNumberTextFormatterOptions options) => Get(options).Format((long)value);

public static string ToBahtText(this decimal value,
ThaiNumberTextFormatterOptions options) => Get(options).GetBahtText(value);

public static string ToThaiWords(this long value, bool useEtWithTensOnly)
=> _formatters[useEtWithTensOnly ? 1 : 0].Format(value);

public static string ToThaiWords(this int value, bool useEtWithTensOnly) => ToThaiWords(value, useEtWithTensOnly);

public static string ToBahtText(this decimal value,
bool useEtWithTensOnly) => _formatters[useEtWithTensOnly ? 1 : 0].GetBahtText(value);
}
}
111 changes: 69 additions & 42 deletions Distractive.Formatters/ThaiNumberTextFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Distractive.Formatters;
Expand All @@ -19,9 +19,11 @@ public sealed class ThaiNumberTextFormatter
new[] { "", "หนึ่งหมื่น", "สองหมื่น", "สามหมื่น", "สี่หมื่น", "ห้าหมื่น", "หกหมื่น", "เจ็ดหมื่น", "แปดหมื่น", "เก้าหมื่น" },
new[] { "", "หนึ่งพัน", "สองพัน", "สามพัน", "สี่พัน", "ห้าพัน", "หกพัน", "เจ็ดพัน", "แปดพัน", "เก้าพัน" },
new[] { "", "หนึ่งร้อย", "สองร้อย", "สามร้อย", "สี่ร้อย", "ห้าร้อย", "หกร้อย", "เจ็ดร้อย", "แปดร้อย", "เก้าร้อย" },
new[] { "", "สิบ", "ยี่สิบ", "สามสิบ", "สี่สิบ", "ห้าสิบ", "หกสิบ", "เจ็ดสิบ", "แปดสิบ", "เก้าสิบ" },
new[] { "", "เอ็ด", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "แปด", "เก้า" },
};
private static readonly string[] _w100k = _numberGrid[0];
private static readonly string[] _w10k = _numberGrid[1];
private static readonly string[] _w1k = _numberGrid[2];
private static readonly string[] _w100 = _numberGrid[3];
private static readonly string[] _numbers = new[] {
"", "หนึ่ง", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "แปด", "เก้า",
"สิบ", "สิบเอ็ด", "สิบสอง", "สิบสาม", "สิบสี่", "สิบห้า", "สิบหก", "สิบเจ็ด", "สิบแปด", "สิบเก้า",
Expand All @@ -42,6 +44,18 @@ public sealed class ThaiNumberTextFormatter
private const string s_Negative = "ลบ";
private const decimal md = 1_000_000M;

private readonly string _et;
public ThaiNumberTextFormatter(ThaiNumberTextFormatterOptions options)
{
Options = options;
_et = options.FormatFlags.HasFlag(ThaiNumberTextFormatFlags.EtWithTensOnly)
? "หนึ่ง" : s_Ed;
}

public ThaiNumberTextFormatter() : this(ThaiNumberTextFormatterOptions.Default) { }

public ThaiNumberTextFormatterOptions Options { get; }

internal ref struct CharBuffer
{
public CharBuffer(Span<char> span)
Expand All @@ -66,54 +80,43 @@ public void Append(string s)
public ReadOnlySpan<char> GetTrimmedSpan() => _span[.._position];
public string AsString() =>
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
new string(GetTrimmedSpan());
new(GetTrimmedSpan());
#else
new string (GetTrimmedSpan().ToArray());
new(GetTrimmedSpan().ToArray());
#endif
}


private static ReadOnlySpan<int> BuildDigits(Span<int> buffer, int value)
{
Debug.Assert(value >= 0);

int i = buffer.Length;
do
{
value = Math.DivRem(value, 10, out var r);
buffer[--i] = r;
} while (value > 0);

return buffer[i..];
}

private static void FormatInternal(scoped ref CharBuffer buffer, long value)
private void FormatInternal(scoped ref CharBuffer buffer, int value)
{
if (value == 0) return;
if (value == 1)
{
buffer.Append("หนึ่ง");
return;
}

Debug.Assert(value > 0);
Debug.Assert(value < 1_000_000);

Span<int> digits = stackalloc int[6];
digits.Clear();
BuildDigits(digits, (int)value);
//var digits = BuildDigits(stackalloc int[6], value);
Debug.Assert(digits.Length > 0);
Debug.Assert(digits.Length <= 6);
var grid = _numberGrid;
for (int i = 0; i < 6; i++)

if (value < 100)
{
var n = digits[i];
buffer.Append(grid[i][n]);
buffer.Append(_numbers[value]);
return;
}

Debug.Assert(value >= 100);

var a = _w100k[value / 100_000 % 10];
var b = _w10k[value / 10_000 % 10];
var c = _w1k[value / 1_000 % 10];
var d = _w100[value / 100 % 10];
var p = value % 100;
var s = p == 1 ? _et : _numbers[p];
buffer.Append(a);
buffer.Append(b);
buffer.Append(c);
buffer.Append(d);
buffer.Append(s);
}

private static void Format(scoped ref CharBuffer buffer, long value, bool fillMil = false)
private void Format(scoped ref CharBuffer buffer, long value, bool fillMil = false)
{
bool isNegative = value < 0;
if (isNegative) value = -value;
Expand All @@ -129,8 +132,8 @@ private static void Format(scoped ref CharBuffer buffer, long value, bool fillMi
const long mil = 1_000_000;
long b3 = value;
long b2 = value / mil;
long b1 = value / mil / mil;
long b0 = value / mil / mil / mil;
long b1 = value / (mil * mil);
long b0 = value / (mil * mil * mil);
Debug.Assert(b0 < 100);

if (b0 > 0)
Expand All @@ -141,20 +144,20 @@ private static void Format(scoped ref CharBuffer buffer, long value, bool fillMi

if (b1 > 0 || fillMil)
{
FormatInternal(ref buffer, b1 % mil);
FormatInternal(ref buffer, (int)(b1 % mil));
buffer.Append("ล้าน");
}

if (b2 > 0 || fillMil)
{
FormatInternal(ref buffer, b2 % mil);
FormatInternal(ref buffer, (int)(b2 % mil));
buffer.Append("ล้าน");
}

if (b3 > 0)
{
var val = b3 % mil;
if (val == 1) buffer.Append(s_Ed);
var val = (int)(b3 % mil);
if (val == 1) buffer.Append(_et);
else FormatInternal(ref buffer, val);
}
}
Expand Down Expand Up @@ -225,3 +228,27 @@ public string GetBahtText(decimal value)
}
}

public sealed record ThaiNumberTextFormatterOptions(ThaiNumberTextFormatFlags FormatFlags = ThaiNumberTextFormatFlags.Default)
{
public static ThaiNumberTextFormatterOptions Default { get; } = new();
public static ThaiNumberTextFormatterOptions EtWithTensOnly { get; } = new(ThaiNumberTextFormatFlags.EtWithTensOnly);
}

[Flags]
public enum ThaiNumberTextFormatFlags
{
/// <summary>
/// ค่าตั้งต้น จะใช้เอ็ดเสมอ (ร้อยเอ็ด พันเอ็ด ล้านเอ็ด ร้อยเอ็ดล้าน ฯลฯ)
/// ซึ่งเป็นรูปแบบที่ราชบัณฑิตยสภาแนะนำ
/// </summary>
Default = 0,

/// <summary>
/// ใช้เอ็ดกับหลักสิบเท่านั้น (ยี่สิบเอ็ด-เก้าสิบเอ็ด) ที่เหลือจะเป็นหนึ่ง เช่น หนึ่งร้อยหนึ่ง
/// </summary>
EtWithTensOnly = 1,

[EditorBrowsable(EditorBrowsableState.Never)]
TotalPerm = EtWithTensOnly * 2,
}

0 comments on commit bf05a24

Please sign in to comment.