From e92e9d088fb6b528e2999f6c96e18607c579373c Mon Sep 17 00:00:00 2001 From: kzrnm Date: Thu, 23 Nov 2023 11:52:07 +0900 Subject: [PATCH 1/2] Add `Parse`/`TryParse` to `ModInt` --- .../Math/ModInt/DynamicModInt.cs | 57 ++++++---- .../Math/ModInt/StaticModInt.cs | 56 ++++++---- .../ac-library-csharp.Test/Math/ModIntTest.cs | 101 +++++++++++++++++- 3 files changed, 174 insertions(+), 40 deletions(-) diff --git a/Source/ac-library-csharp/Math/ModInt/DynamicModInt.cs b/Source/ac-library-csharp/Math/ModInt/DynamicModInt.cs index b0d0772..c1ffa40 100644 --- a/Source/ac-library-csharp/Math/ModInt/DynamicModInt.cs +++ b/Source/ac-library-csharp/Math/ModInt/DynamicModInt.cs @@ -239,6 +239,35 @@ public DynamicModInt Inv() [MethodImpl(256)] public bool Equals(DynamicModInt other) => Value == other.Value; public override int GetHashCode() => _v.GetHashCode(); + public static bool TryParse(ReadOnlySpan s, out DynamicModInt result) + { + result = Zero; + DynamicModInt 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 Parse(ReadOnlySpan s) + { + if (!TryParse(s, out var r)) + Throw(); + return r; + void Throw() => throw new FormatException(); + } + #if GENERIC_MATH static int INumberBase>.Radix => 2; static DynamicModInt IAdditiveIdentity, DynamicModInt>.AdditiveIdentity => default; @@ -265,24 +294,16 @@ public DynamicModInt Inv() static DynamicModInt INumberBase>.MaxMagnitudeNumber(DynamicModInt x, DynamicModInt y) => new DynamicModInt(uint.Max(x._v, y._v)); static DynamicModInt INumberBase>.MinMagnitude(DynamicModInt x, DynamicModInt y) => new DynamicModInt(uint.Min(x._v, y._v)); static DynamicModInt INumberBase>.MinMagnitudeNumber(DynamicModInt x, DynamicModInt y) => new DynamicModInt(uint.Min(x._v, y._v)); - static DynamicModInt INumberBase>.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider); - static DynamicModInt INumberBase>.Parse(string s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider); - static DynamicModInt ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider provider) => long.Parse(s, provider); - static DynamicModInt IParsable>.Parse(string s, IFormatProvider provider) => long.Parse(s, provider); - static bool ISpanParsable>.TryParse(ReadOnlySpan s, IFormatProvider provider, out DynamicModInt result) - => TryParse(s, NumberStyles.None, provider, out result); - static bool IParsable>.TryParse(string s, IFormatProvider provider, out DynamicModInt result) - => TryParse(s, NumberStyles.None, provider, out result); - static bool INumberBase>.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out DynamicModInt result) - => TryParse(s, style, provider, out result); - static bool INumberBase>.TryParse(string s, NumberStyles style, IFormatProvider provider, out DynamicModInt result) - => TryParse(s, style, provider, out result); - private static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out DynamicModInt result) - { - var b = long.TryParse(s, style, provider, out var r); - result = r; - return b; - } + + static DynamicModInt INumberBase>.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => Parse(s); + static DynamicModInt INumberBase>.Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s); + static DynamicModInt ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider provider) => Parse(s); + static DynamicModInt IParsable>.Parse(string s, IFormatProvider provider) => Parse(s); + static bool ISpanParsable>.TryParse(ReadOnlySpan s, IFormatProvider provider, out DynamicModInt result) => TryParse(s, out result); + static bool IParsable>.TryParse(string s, IFormatProvider provider, out DynamicModInt result) => TryParse(s, out result); + static bool INumberBase>.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out DynamicModInt result) => TryParse(s, out result); + static bool INumberBase>.TryParse(string s, NumberStyles style, IFormatProvider provider, out DynamicModInt result) => TryParse(s, out result); + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) => _v.TryFormat(destination, out charsWritten, format, provider); diff --git a/Source/ac-library-csharp/Math/ModInt/StaticModInt.cs b/Source/ac-library-csharp/Math/ModInt/StaticModInt.cs index 7125c0c..c74c461 100644 --- a/Source/ac-library-csharp/Math/ModInt/StaticModInt.cs +++ b/Source/ac-library-csharp/Math/ModInt/StaticModInt.cs @@ -222,6 +222,34 @@ public StaticModInt Inv() public override bool Equals(object obj) => obj is StaticModInt m && Equals(m); [MethodImpl(256)] public bool Equals(StaticModInt other) => _v == other._v; public override int GetHashCode() => _v.GetHashCode(); + public static bool TryParse(ReadOnlySpan s, out StaticModInt result) + { + result = Zero; + StaticModInt 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 Parse(ReadOnlySpan s) + { + if (!TryParse(s, out var r)) + Throw(); + return r; + void Throw() => throw new FormatException(); + } #if GENERIC_MATH static int INumberBase>.Radix => 2; @@ -249,24 +277,16 @@ public StaticModInt Inv() static StaticModInt INumberBase>.MaxMagnitudeNumber(StaticModInt x, StaticModInt y) => new StaticModInt(uint.Max(x._v, y._v)); static StaticModInt INumberBase>.MinMagnitude(StaticModInt x, StaticModInt y) => new StaticModInt(uint.Min(x._v, y._v)); static StaticModInt INumberBase>.MinMagnitudeNumber(StaticModInt x, StaticModInt y) => new StaticModInt(uint.Min(x._v, y._v)); - static StaticModInt INumberBase>.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider); - static StaticModInt INumberBase>.Parse(string s, NumberStyles style, IFormatProvider provider) => long.Parse(s, style, provider); - static StaticModInt ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider provider) => long.Parse(s, provider); - static StaticModInt IParsable>.Parse(string s, IFormatProvider provider) => long.Parse(s, provider); - static bool ISpanParsable>.TryParse(ReadOnlySpan s, IFormatProvider provider, out StaticModInt result) - => TryParse(s, NumberStyles.None, provider, out result); - static bool IParsable>.TryParse(string s, IFormatProvider provider, out StaticModInt result) - => TryParse(s, NumberStyles.None, provider, out result); - static bool INumberBase>.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out StaticModInt result) - => TryParse(s, style, provider, out result); - static bool INumberBase>.TryParse(string s, NumberStyles style, IFormatProvider provider, out StaticModInt result) - => TryParse(s, style, provider, out result); - private static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out StaticModInt result) - { - var b = long.TryParse(s, style, provider, out var r); - result = r; - return b; - } + + static StaticModInt INumberBase>.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => Parse(s); + static StaticModInt INumberBase>.Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s); + static StaticModInt ISpanParsable>.Parse(ReadOnlySpan s, IFormatProvider provider) => Parse(s); + static StaticModInt IParsable>.Parse(string s, IFormatProvider provider) => Parse(s); + static bool ISpanParsable>.TryParse(ReadOnlySpan s, IFormatProvider provider, out StaticModInt result) => TryParse(s, out result); + static bool IParsable>.TryParse(string s, IFormatProvider provider, out StaticModInt result) => TryParse(s, out result); + static bool INumberBase>.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out StaticModInt result) => TryParse(s, out result); + static bool INumberBase>.TryParse(string s, NumberStyles style, IFormatProvider provider, out StaticModInt result) => TryParse(s, out result); + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) => _v.TryFormat(destination, out charsWritten, format, provider); diff --git a/Test/ac-library-csharp.Test/Math/ModIntTest.cs b/Test/ac-library-csharp.Test/Math/ModIntTest.cs index f091dd8..85813a7 100644 --- a/Test/ac-library-csharp.Test/Math/ModIntTest.cs +++ b/Test/ac-library-csharp.Test/Math/ModIntTest.cs @@ -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 { @@ -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(); + RunStatic(); + RunStatic(); + RunStatic(); + RunStatic(); + + DynamicModInt.Mod = 998244353; + RunDynamic(); + + DynamicModInt.Mod = 1000000008; + RunDynamic(); + + void RunStatic() 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.TryParse(s, out var num1).Should().BeTrue(); + var num2 = StaticModInt.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.TryParse(s, out var num1).Should().BeTrue(); + var num2 = StaticModInt.Parse(s); + + num1.Value.Should().Be(expected); + num2.Value.Should().Be(expected); + } + + foreach (var s in invalids) + { + StaticModInt.TryParse(s, out _).Should().BeFalse(); + s.Invoking(s => StaticModInt.Parse(s)).Should().ThrowExactly(); + } + } + + void RunDynamic() where T : struct + { + var mod = DynamicModInt.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.TryParse(s, out var num1).Should().BeTrue(); + var num2 = DynamicModInt.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.TryParse(s, out var num1).Should().BeTrue(); + var num2 = DynamicModInt.Parse(s); + + num1.Value.Should().Be(expected); + num2.Value.Should().Be(expected); + } + + foreach (var s in invalids) + { + DynamicModInt.TryParse(s, out _).Should().BeFalse(); + s.Invoking(s => DynamicModInt.Parse(s)).Should().ThrowExactly(); + } + } + } + private struct DynamicUsageID { } [Fact] @@ -314,6 +406,7 @@ public void ConstructorStatic() (1 + new StaticModInt(1)).Value.Should().Be(2); } + private struct MemoryID : IStaticMod { public uint Mod => 101; From 729e3308ad33cf249b28e7e98e045491e0a97990 Mon Sep 17 00:00:00 2001 From: kzrnm Date: Thu, 23 Nov 2023 11:52:26 +0900 Subject: [PATCH 2/2] v3.6.0 --- CHANGELOG.md | 4 ++++ Directory.Build.props | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a76416..8d89511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Directory.Build.props b/Directory.Build.props index 48d7850..e6b1222 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,8 +7,8 @@ https://github.com/kzrnm/ac-library-csharp https://github.com/kzrnm/ac-library-csharp/blob/main/CHANGELOG.md - 3.5.0 - 3.5.0.101 + 3.6.0 + 3.6.0.101 $(GIT_COMMIT) True