Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Parse/TryParse to ModInt #109

Merged
merged 2 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.6.0] - 2023-11-23
### Added
- Add `Parse`/`TryParse` to `ModInt`

## [3.5.0] - 2023-10-29
### Added
- Add MfGraph.Count
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<RepositoryUrl>https://github.com/kzrnm/ac-library-csharp</RepositoryUrl>
<PackageReleaseNotes>https://github.com/kzrnm/ac-library-csharp/blob/main/CHANGELOG.md</PackageReleaseNotes>

<Version>3.5.0</Version>
<AssemblyVersion>3.5.0.101</AssemblyVersion>
<Version>3.6.0</Version>
<AssemblyVersion>3.6.0.101</AssemblyVersion>
<RepositoryCommit Condition="'$(GIT_COMMIT)' != ''">$(GIT_COMMIT)</RepositoryCommit>

<SignAssembly>True</SignAssembly>
Expand Down
57 changes: 39 additions & 18 deletions Source/ac-library-csharp/Math/ModInt/DynamicModInt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,35 @@ public DynamicModInt<T> Inv()
[MethodImpl(256)] public bool Equals(DynamicModInt<T> other) => Value == other.Value;
public override int GetHashCode() => _v.GetHashCode();

public static bool TryParse(ReadOnlySpan<char> s, out DynamicModInt<T> result)
{
result = Zero;
DynamicModInt<T> ten = 10u;
s = s.Trim();
bool minus = false;
if (s.Length > 0 && s[0] == '-')
{
minus = true;
s = s.Slice(1);
}
for (int i = 0; i < s.Length; i++)
{
var d = (uint)(s[i] - '0');
if (d >= 10) return false;
result = result * ten + d;
}
if (minus)
result = -result;
return true;
}
public static DynamicModInt<T> Parse(ReadOnlySpan<char> s)
{
if (!TryParse(s, out var r))
Throw();
return r;
void Throw() => throw new FormatException();
}

#if GENERIC_MATH
static int INumberBase<DynamicModInt<T>>.Radix => 2;
static DynamicModInt<T> IAdditiveIdentity<DynamicModInt<T>, DynamicModInt<T>>.AdditiveIdentity => default;
Expand All @@ -265,24 +294,16 @@ public DynamicModInt<T> Inv()
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.MaxMagnitudeNumber(DynamicModInt<T> x, DynamicModInt<T> y) => new DynamicModInt<T>(uint.Max(x._v, y._v));
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.MinMagnitude(DynamicModInt<T> x, DynamicModInt<T> y) => new DynamicModInt<T>(uint.Min(x._v, y._v));
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.MinMagnitudeNumber(DynamicModInt<T> x, DynamicModInt<T> y) => new DynamicModInt<T>(uint.Min(x._v, y._v));
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider);
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.Parse(string s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider);
static DynamicModInt<T> ISpanParsable<DynamicModInt<T>>.Parse(ReadOnlySpan<char> s, IFormatProvider provider) => long.Parse(s, provider);
static DynamicModInt<T> IParsable<DynamicModInt<T>>.Parse(string s, IFormatProvider provider) => long.Parse(s, provider);
static bool ISpanParsable<DynamicModInt<T>>.TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out DynamicModInt<T> result)
=> TryParse(s, NumberStyles.None, provider, out result);
static bool IParsable<DynamicModInt<T>>.TryParse(string s, IFormatProvider provider, out DynamicModInt<T> result)
=> TryParse(s, NumberStyles.None, provider, out result);
static bool INumberBase<DynamicModInt<T>>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out DynamicModInt<T> result)
=> TryParse(s, style, provider, out result);
static bool INumberBase<DynamicModInt<T>>.TryParse(string s, NumberStyles style, IFormatProvider provider, out DynamicModInt<T> result)
=> TryParse(s, style, provider, out result);
private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out DynamicModInt<T> result)
{
var b = long.TryParse(s, style, provider, out var r);
result = r;
return b;
}

static DynamicModInt<T> INumberBase<DynamicModInt<T>>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider) => Parse(s);
static DynamicModInt<T> INumberBase<DynamicModInt<T>>.Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s);
static DynamicModInt<T> ISpanParsable<DynamicModInt<T>>.Parse(ReadOnlySpan<char> s, IFormatProvider provider) => Parse(s);
static DynamicModInt<T> IParsable<DynamicModInt<T>>.Parse(string s, IFormatProvider provider) => Parse(s);
static bool ISpanParsable<DynamicModInt<T>>.TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out DynamicModInt<T> result) => TryParse(s, out result);
static bool IParsable<DynamicModInt<T>>.TryParse(string s, IFormatProvider provider, out DynamicModInt<T> result) => TryParse(s, out result);
static bool INumberBase<DynamicModInt<T>>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out DynamicModInt<T> result) => TryParse(s, out result);
static bool INumberBase<DynamicModInt<T>>.TryParse(string s, NumberStyles style, IFormatProvider provider, out DynamicModInt<T> result) => TryParse(s, out result);

bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider) => _v.TryFormat(destination, out charsWritten, format, provider);


Expand Down
56 changes: 38 additions & 18 deletions Source/ac-library-csharp/Math/ModInt/StaticModInt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,34 @@ public StaticModInt<T> Inv()
public override bool Equals(object obj) => obj is StaticModInt<T> m && Equals(m);
[MethodImpl(256)] public bool Equals(StaticModInt<T> other) => _v == other._v;
public override int GetHashCode() => _v.GetHashCode();
public static bool TryParse(ReadOnlySpan<char> s, out StaticModInt<T> result)
{
result = Zero;
StaticModInt<T> ten = 10u;
s = s.Trim();
bool minus = false;
if (s.Length > 0 && s[0] == '-')
{
minus = true;
s = s.Slice(1);
}
for (int i = 0; i < s.Length; i++)
{
var d = (uint)(s[i] - '0');
if (d >= 10) return false;
result = result * ten + d;
}
if (minus)
result = -result;
return true;
}
public static StaticModInt<T> Parse(ReadOnlySpan<char> s)
{
if (!TryParse(s, out var r))
Throw();
return r;
void Throw() => throw new FormatException();
}

#if GENERIC_MATH
static int INumberBase<StaticModInt<T>>.Radix => 2;
Expand Down Expand Up @@ -249,24 +277,16 @@ public StaticModInt<T> Inv()
static StaticModInt<T> INumberBase<StaticModInt<T>>.MaxMagnitudeNumber(StaticModInt<T> x, StaticModInt<T> y) => new StaticModInt<T>(uint.Max(x._v, y._v));
static StaticModInt<T> INumberBase<StaticModInt<T>>.MinMagnitude(StaticModInt<T> x, StaticModInt<T> y) => new StaticModInt<T>(uint.Min(x._v, y._v));
static StaticModInt<T> INumberBase<StaticModInt<T>>.MinMagnitudeNumber(StaticModInt<T> x, StaticModInt<T> y) => new StaticModInt<T>(uint.Min(x._v, y._v));
static StaticModInt<T> INumberBase<StaticModInt<T>>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider);
static StaticModInt<T> INumberBase<StaticModInt<T>>.Parse(string s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider);
static StaticModInt<T> ISpanParsable<StaticModInt<T>>.Parse(ReadOnlySpan<char> s, IFormatProvider provider) => long.Parse(s, provider);
static StaticModInt<T> IParsable<StaticModInt<T>>.Parse(string s, IFormatProvider provider) => long.Parse(s, provider);
static bool ISpanParsable<StaticModInt<T>>.TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out StaticModInt<T> result)
=> TryParse(s, NumberStyles.None, provider, out result);
static bool IParsable<StaticModInt<T>>.TryParse(string s, IFormatProvider provider, out StaticModInt<T> result)
=> TryParse(s, NumberStyles.None, provider, out result);
static bool INumberBase<StaticModInt<T>>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out StaticModInt<T> result)
=> TryParse(s, style, provider, out result);
static bool INumberBase<StaticModInt<T>>.TryParse(string s, NumberStyles style, IFormatProvider provider, out StaticModInt<T> result)
=> TryParse(s, style, provider, out result);
private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out StaticModInt<T> result)
{
var b = long.TryParse(s, style, provider, out var r);
result = r;
return b;
}

static StaticModInt<T> INumberBase<StaticModInt<T>>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider) => Parse(s);
static StaticModInt<T> INumberBase<StaticModInt<T>>.Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s);
static StaticModInt<T> ISpanParsable<StaticModInt<T>>.Parse(ReadOnlySpan<char> s, IFormatProvider provider) => Parse(s);
static StaticModInt<T> IParsable<StaticModInt<T>>.Parse(string s, IFormatProvider provider) => Parse(s);
static bool ISpanParsable<StaticModInt<T>>.TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out StaticModInt<T> result) => TryParse(s, out result);
static bool IParsable<StaticModInt<T>>.TryParse(string s, IFormatProvider provider, out StaticModInt<T> result) => TryParse(s, out result);
static bool INumberBase<StaticModInt<T>>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out StaticModInt<T> result) => TryParse(s, out result);
static bool INumberBase<StaticModInt<T>>.TryParse(string s, NumberStyles style, IFormatProvider provider, out StaticModInt<T> result) => TryParse(s, out result);

bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider) => _v.TryFormat(destination, out charsWritten, format, provider);


Expand Down
101 changes: 97 additions & 4 deletions Test/ac-library-csharp.Test/Math/ModIntTest.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using AtCoder.Internal;
using FluentAssertions;
using MersenneTwister;
using Xunit;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif

namespace AtCoder
{
Expand Down Expand Up @@ -261,6 +260,99 @@ public void Increment()
}
}

[Fact]
public void Parse()
{
var bigs = new[]{
BigInteger.Pow(10, 1000),
BigInteger.Pow(10, 1000),
};
var invalids = new[]
{
"2-2",
"ABC",
"111.0",
"111,1",
};
RunStatic<ModID11>();
RunStatic<ModID12>();
RunStatic<ModID1000000007>();
RunStatic<ModID1000000008>();
RunStatic<ModID998244353>();

DynamicModInt<ModID998244353>.Mod = 998244353;
RunDynamic<ModID998244353>();

DynamicModInt<ModID1000000008>.Mod = 1000000008;
RunDynamic<ModID1000000008>();

void RunStatic<T>() where T : struct, IStaticMod
{
var mod = (int)new T().Mod;
var nums = Enumerable.Range(-100, 200).Concat(Enumerable.Range(mod - 100, 200));
foreach (var n in nums)
{
var s = n.ToString();
var expected = (n % mod + mod) % mod;
StaticModInt<T>.TryParse(s, out var num1).Should().BeTrue();
var num2 = StaticModInt<T>.Parse(s);

num1.Value.Should().Be(expected);
num2.Value.Should().Be(expected);
}

foreach (var n in bigs)
{
var s = n.ToString();
var expected = (int)(n % mod + mod) % mod;
StaticModInt<T>.TryParse(s, out var num1).Should().BeTrue();
var num2 = StaticModInt<T>.Parse(s);

num1.Value.Should().Be(expected);
num2.Value.Should().Be(expected);
}

foreach (var s in invalids)
{
StaticModInt<T>.TryParse(s, out _).Should().BeFalse();
s.Invoking(s => StaticModInt<T>.Parse(s)).Should().ThrowExactly<FormatException>();
}
}

void RunDynamic<T>() where T : struct
{
var mod = DynamicModInt<T>.Mod;
var nums = Enumerable.Range(-100, 200).Concat(Enumerable.Range(mod - 100, 200));
foreach (var n in nums)
{
var s = n.ToString();
var expected = (n % mod + mod) % mod;
DynamicModInt<T>.TryParse(s, out var num1).Should().BeTrue();
var num2 = DynamicModInt<T>.Parse(s);

num1.Value.Should().Be(expected);
num2.Value.Should().Be(expected);
}

foreach (var n in bigs)
{
var s = n.ToString();
var expected = (int)(n % mod + mod) % mod;
DynamicModInt<T>.TryParse(s, out var num1).Should().BeTrue();
var num2 = DynamicModInt<T>.Parse(s);

num1.Value.Should().Be(expected);
num2.Value.Should().Be(expected);
}

foreach (var s in invalids)
{
DynamicModInt<T>.TryParse(s, out _).Should().BeFalse();
s.Invoking(s => DynamicModInt<T>.Parse(s)).Should().ThrowExactly<FormatException>();
}
}
}


private struct DynamicUsageID { }
[Fact]
Expand Down Expand Up @@ -314,6 +406,7 @@ public void ConstructorStatic()
(1 + new StaticModInt<ConstructorID>(1)).Value.Should().Be(2);
}


private struct MemoryID : IStaticMod
{
public uint Mod => 101;
Expand Down
Loading