Skip to content

Commit

Permalink
Merge pull request #109 from kzrnm/feature/paser_modint
Browse files Browse the repository at this point in the history
Add Parse/TryParse to ModInt
  • Loading branch information
kzrnm authored Nov 23, 2023
2 parents 8d6fe8b + 729e330 commit 2bca9fc
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 42 deletions.
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

0 comments on commit 2bca9fc

Please sign in to comment.