using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static Tmpl; using System.Globalization; using System.Diagnostics; StreamWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false }; Console.SetOut(writer); Solver.Solve(); Console.Out.Flush(); public static class Solver { private static readonly AtCoderIO cin = new AtCoderIO(); public static unsafe void Solve() { int N = cin.Int(); long[] A = cin.LongArray(N); ModInt[] invA = new ModInt[N]; for (int i = 0; i < N; i++) { invA[i] = ((ModInt)A[i]).Inv(); } Ensure(1 <= N && N <= 1500); Array.Sort(A); for (int i = 0; i < N; i++) { if (i < N - 1) { Ensure(A[i] != A[i + 1]); } } ModInt ans = 0L; for (int i = 0; i < N; i++) { ans += (A[i] + 1) / (ModInt)2L; } if ((long)A[0] <= N + 2) { print(SolveNaive(N, A, invA)); return; } ModInt[] F = new ModInt[N + 3]; for (int x = 1; x <= N + 2; x++) { ModInt left = 1L; ModInt right = 1L; for (int i = 1; i < N; i++) { right *= (A[i] - x) * invA[i]; } for (int k = 0; k < N; k++) { F[x] += (A[k] - x) * left * right * invA[k]; left *= (A[k] - x + 1) * invA[k]; if (k < N - 1) { right /= (ModInt)(A[k + 1] - x) * invA[k + 1]; } } } for (int j = 2; j <= N + 2; j++) { F[j] += F[j - 1]; } ModCache cache = new(N + 100); ModInt[] prefix = new ModInt[N + 3]; ModInt[] suffix = new ModInt[N + 3]; prefix[0] = 1L; suffix[N + 2] = 1L; for (int i = 1; i <= N + 2; i++) { prefix[i] = prefix[i - 1] * (A[0] - i); } for (int i = N + 1; i >= 0; i--) { suffix[i] = suffix[i + 1] * (A[0] - i - 1); } for (int i = 1; i <= N + 2; i++) { long sign = (i - N + 2) % 2 == 0 ? 1 : -1; ans += F[i] * (prefix[i - 1] * suffix[i]) * (cache.InverseFactorial(i - 1) * sign * cache.InverseFactorial(N + 2 - i)); } print(ans); } private static ModInt SolveNaive(int N, long[] A, ModInt[] invA) { ModInt ans = 0L; for (int i = 0; i < N; i++) { ans += (A[i] + 1) / (ModInt)2L; } for (int m = 1; m <= A[0]; m++) { ModInt left = 1L; ModInt right = 1L; for (int i = 1; i < N; i++) { right *= (A[i] - m) * invA[i]; } for (int k = 0; k < N; k++) { ans += (A[k] - m) * left * right * invA[k]; left *= (A[k] - m + 1) * invA[k]; if (k < N - 1) { right /= (ModInt)(A[k + 1] - m) * invA[k + 1]; } } } return ans; } private static void Ensure(bool condition) { if (!condition) { throw new InvalidOperationException(); } } } /// /// ModIntの階乗、階乗の逆元、逆元を前計算。 /// public sealed class ModCache where T : struct, IMod { private ModInt[] _factorial; private ModInt[] _inverseFactorial; private ModInt[] _inverse; // 階乗(&階乗逆元)と逆元を前計算する. // O(max) public ModCache(long max) { _factorial = new ModInt[max + 1]; _inverseFactorial = new ModInt[max + 1]; _factorial[0] = 1; _inverseFactorial[0] = ModInt.One; _inverse = new ModInt[max + 1]; _inverse[1] = ModInt.CreateFast(1); for (long p = 1; p <= max; p++) { _factorial[p] = _factorial[p - 1] * p; if (p > 1) { _inverse[p] = -(ModInt.Mod / p) * _inverse[ModInt.Mod % p]; } _inverseFactorial[p] = _inverseFactorial[p - 1] * _inverse[p]; } } /// /// binom(n, r)を返す。r<0またはr>nのとき0を返す。計算量: O(1) /// /// /// /// public ModInt Combination(long n, long r) { if (r < 0 || r > n) return 0; return _factorial[n] * (_inverseFactorial[n - r] * _inverseFactorial[r]); } /// /// nPrを返す。r<0またはr>nのとき0を返す。計算量: O(1) /// /// /// /// public ModInt Permutation(long n, long r) { if (r < 0 || r > n) return 1; return _factorial[n] * _inverseFactorial[n - r]; } /// /// n!の値を返す。計算量: O(1) /// /// /// /// public ModInt Factorial(long n) { Debug.Assert(0 <= n && n < _factorial.Length); return _factorial[n]; } /// /// n!の乗法逆元(n!)^{-1}の値を返す。計算量: O(1) /// /// /// /// public ModInt InverseFactorial(int n) { Debug.Assert(0 <= n && n < _inverseFactorial.Length); return _inverseFactorial[n]; } /// /// nの乗法逆元n^{-1}を返す。計算量: O(1) /// /// /// /// public ModInt Inverse(long n) { Debug.Assert(0 <= n && n < _inverse.Length); return _inverse[n]; } } public sealed class AtCoderIO { Queue _readQueue = new Queue(); private void LoadQueue() { if (_readQueue.Count > 0) return; string line = Console.ReadLine(); string[] split = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < split.Length; i++) _readQueue.Enqueue(split[i]); } private void Guard() { if (_readQueue.Count == 0) { throw new Exception("NO DATA TO READ"); } } public int Int() { LoadQueue(); Guard(); return int.Parse(_readQueue.Dequeue()); } public long Long() { LoadQueue(); Guard(); return long.Parse(_readQueue.Dequeue()); } public string String() { LoadQueue(); Guard(); return _readQueue.Dequeue(); } public short Short() { LoadQueue(); Guard(); return short.Parse(_readQueue.Dequeue()); } public byte Byte() { LoadQueue(); Guard(); return byte.Parse(_readQueue.Dequeue()); } public char Char() { LoadQueue(); Guard(); return char.Parse(_readQueue.Dequeue()); } public double Double() { LoadQueue(); Guard(); return double.Parse(_readQueue.Dequeue()); } public float Float() { LoadQueue(); Guard(); return float.Parse(_readQueue.Dequeue()); } public ModInt ModInt() where T : struct, IMod { return new ModInt(Long()); } public T Read() { Type type = typeof(T); if (type == typeof(int)) return (T)(object)Int(); else if (type == typeof(long)) return (T)(object)Long(); else if (type == typeof(float)) return (T)(object)Float(); else if (type == typeof(double)) return (T)(object)Double(); else if (type == typeof(short)) return (T)(object)Short(); else if (type == typeof(byte)) return (T)(object)Byte(); else if (type == typeof(char)) return (T)(object)Char(); else if (type == typeof(string)) return (T)(object)String(); else return default(T); } public int[] IntArray(int n) { if (n == 0) return Array.Empty(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = Int(); } return arr; } public int[] ZeroIndexedPermutation(int n) { if (n == 0) return Array.Empty(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = Int() - 1; } return arr; } public long[] LongArray(int n) { if (n == 0) return Array.Empty(); long[] arr = new long[n]; for (int i = 0; i < n; i++) { arr[i] = Long(); } return arr; } public double[] DoubleArray(int n) { if (n == 0) return Array.Empty(); double[] arr = new double[n]; for (int i = 0; i < n; i++) { arr[i] = Double(); } return arr; } public ModInt[] ModIntArray(int n) where T : struct, IMod { if (n == 0) return Array.Empty>(); ModInt[] arr = new ModInt[n]; for (int i = 0; i < n; i++) { arr[i] = (ModInt)Long(); } return arr; } public T[] ReadArray(int n) { if (n == 0) return Array.Empty(); T[] arr = new T[n]; for (int i = 0; i < n; i++) { arr[i] = Read(); } return arr; } } public static class Tmpl { public static long pow(long b, long exp) { if (exp == 0) return 1; if (exp == 1) return b; long m = pow(b, exp / 2L); m *= m; if (exp % 2L == 1) m *= b; return m; } public static long modpow(long b, long exp, long mod) { if (exp == 0) return 1; if (exp == 1) return b % mod; long m = modpow(b, exp / 2L, mod); m *= m; m %= mod; if (exp % 2L == 1) m *= b % mod; m %= mod; return m; } static long modinv( long a, long mod ) { long b = mod, u = 1, v = 0; while ( b > 0 ) { long t = a / b; a -= t * b; swap( ref a, ref b ); u -= t * v; swap( ref u, ref v ); } u %= mod; if (u < 0) u += mod; return u; } public static long calcLcm(long a, long b) { return a * b / calcGcd(a, b); } public static long calcGcd(long a, long b) { if (a % b == 0) return b; return calcGcd(b, a % b); } // ax ≡ b (mod m)なる最小のxを求める public static bool solveModLinear(long a, long b, long m, out long minSolution) { long gcd = calcGcd(calcGcd(a, b), m); a /= gcd; b /= gcd; m /= gcd; if (calcGcd(a, m) == 1) { long inv = modinv(a, m); minSolution = (b * inv) % m; return true; } minSolution = 0; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void YesNo(bool t) { Console.WriteLine(t ? "Yes" : "No"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void YESNO(bool t) { Console.WriteLine(t ? "YES" : "NO"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void yesno(bool t) { Console.WriteLine(t ? "yes" : "no"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool checkBit(int bit, int n) { return ((1 << n) & bit) != 0; } public static bool nextSubset(ref int set, int super) { set = (set - 1) & super; return set != 0; } public static bool nextCombination(int n, int r, int[] array) { int i = array.Length - 1; while (i >= 0 && array[i] == i + n - r) { i--; } if (i < 0) return false; array[i]++; for (int j = i + 1; j < r; j++) { array[j] = array[j - 1] + 1; } return true; } public static bool nextPermutation(T[] array, int start, int length) where T : IComparable { int end = start + length - 1; if (end <= start) return false; int last = end; while (true) { int pos = last--; if (array[last].CompareTo(array[pos]) < 0) { int i; for (i = end + 1; array[last].CompareTo(array[--i]) >= 0; ) { } T tmp = array[last]; array[last] = array[i]; array[i] = tmp; Array.Reverse(array, pos, end - pos + 1); return true; } if (last == start) { Array.Reverse(array, start, end - start); return false; } } throw new Exception("NextPermutation: Fatal error"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool chmax (ref T target, T value) where T : struct, IComparisonOperators { if (value > target) { target = value; return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool chmin(ref T target, T value) where T : struct, IComparisonOperators { if (value < target) { target = value; return true; } return false; } public static ReadOnlySpan spanOf(List list) { return CollectionsMarshal.AsSpan(list); } public static int lowerBound(T[] array, int start, int end, T value) where T : struct, IComparisonOperators { int low = start; int high = end; int mid; while (low < high) { mid = ((high - low) >> 1) + low; if (array[mid] < value) low = mid + 1; else high = mid; } if (low == array.Length - 1 && array[low] < value) { return array.Length; } return low; } public static int lowerBound(List array, int start, int end, T value) where T : struct, IComparisonOperators { int low = start; int high = end; int mid; while (low < high) { mid = ((high - low) >> 1) + low; if (array[mid] < value) low = mid + 1; else high = mid; } if (low == array.Count - 1 && array[low] < value) { return array.Count; } return low; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void swap(ref T a, ref T b) where T : struct { T temp = a; a = b; b = temp; } public static int[] compressCoords(T[] a) where T : struct, IComparisonOperators { T[] b = a.OrderBy(x => x).Distinct().ToArray(); int[] result = new int[a.Length]; for (int i = 0; i < a.Length; i++) { result[i] = lowerBound(b, 0, b.Length - 1, a[i]); } return result; } public static List> compressRunLength(T[] array) where T : struct, IEquatable { List> list = new List>(); int count = 1; int start = 0; T prev = array[0]; for (int i = 1; i < array.Length; i++) { if (prev.Equals(array[i])) { count++; } else { list.Add(new RunLengthElement(start, count, prev)); start = i; count = 1; } prev = array[i]; } list.Add(new RunLengthElement(start, count, prev)); return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(IEnumerable array, char delimiter = ' ') { Console.WriteLine(string.Join(delimiter, array)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void fprint(IEnumerable array, char delimiter = ' ') { print(array); Console.Out.Flush(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(string msg) { Console.WriteLine(msg); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void fprint(string msg) { Console.WriteLine(msg); Console.Out.Flush(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(int val) { print(val.ToString()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(long val) { print(val.ToString()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(ModInt val) where T : struct, IMod { print(val.ToString()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void fprint(int val) { print(val.ToString()); Console.Out.Flush(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void fprint(long val) { print(val.ToString()); Console.Out.Flush(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void fprint(ModInt val) where T : struct, IMod { fprint(val.ToString()); Console.Out.Flush(); } public static void print(T[,] grid) { for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { Console.Write(grid[y, x] + "\t"); } Console.WriteLine(); } } public static void print01(bool t) { Console.WriteLine(t ? "1" : "0"); } public static TSrc lowerBoundKth(TSrc min, TSrc max, Func f, TCnt bound) where TSrc: struct, INumber where TCnt: struct, INumber { TSrc left = min; TSrc right = max; TSrc two = TSrc.CreateChecked(2); TSrc one = TSrc.CreateChecked(1); while (right > left) { TSrc mid = left + (right - left) / two; TCnt c = f(mid); if (c < bound) { left = mid + one; } else { right = mid; } } return left; } public static bool isSquareNumber(long n) { long q = (long)Math.Floor(Math.Sqrt(n)); return q * q == n; } public static T[][] makeDPTable2D(int length1, int length2, T value) { bool fill = !default(T).Equals(value); T[][] table = new T[length1][]; for (int i = 0; i < length1; i++) { table[i] = new T[length2]; if (fill) { Array.Fill(table[i], value); } } return table; } public static T[][][] makeDPTable3D(int length1, int length2, int length3, T value) { bool fill = !default(T).Equals(value); T[][][] table = new T[length1][][]; for (int i = 0; i < length1; i++) { table[i] = new T[length2][]; for (int j = 0; j < length2; j++) { table[i][j] = new T[length3]; if (fill) { Array.Fill(table[i][j], value); } } } return table; } } public readonly struct RunLengthElement where T : struct { public readonly int Count; public readonly int StartIndex; public readonly T Value; public RunLengthElement(int startIndex, int count, T value) { this.StartIndex = startIndex; this.Count = count; this.Value = value; } } /// /// 自動でmodをとる整数。必要ないならあんまり使いすぎないように。 /// パフォーマンスはあまり良くないかもしれない。 /// /// public readonly struct ModInt : INumber> where T : struct, IMod { public static long Mod => _mod.Mod; private static readonly T _mod = default; public readonly long Value; /// /// 1を返す。計算量: O(1) /// public static ModInt One { get; } = CreateFast(1L); /// /// 0を返す。計算量: O(1) /// public static ModInt Zero { get; } = CreateFast(0L); public static int Radix => 10; public static ModInt MinValue => CreateFast(0L); public static ModInt MaxValue => CreateFast(_mod.Mod - 1); /// /// 加法単位元つまり0を返す。計算量: O(1) /// public static ModInt AdditiveIdentity { get; } = CreateFast(0L); public static ModInt MultiplicativeIdentity { get; } = CreateFast(1L); public ModInt(long value) { Value = SafeMod(value); } private ModInt(long value, bool dummy) { Value = value; } /// /// modintを構築する。0 <= value < MODのとき限定。速いはず。計算量: O(1) /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ModInt CreateFast(long value) { return new ModInt(value, false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static long SafeMod(long a) { return a % _mod.Mod + ((a >> 63) & _mod.Mod); } /// /// exp乗を計算する。計算量: O(log(exp)) /// /// /// public readonly ModInt Power(long exp) { if (exp < 0) { return Power(-exp).Inv(); } else { long res = 1L; long b = Value; while (exp > 0) { if ((exp & 1) == 1) res = res * b % _mod.Mod; b = b * b % _mod.Mod; exp >>= 1; } return CreateFast(res); } } /// /// 乗法逆元を返す。0は渡さない。計算量: O(logN) /// /// public readonly ModInt Inv() { long x = 1, y = 0; long x1 = 0, y1 = 1; long b = _mod.Mod; long a = Value; while (b != 0) { long q = a / b; long t = a % b; a = b; b = t; long tx = x - q * x1; long ty = y - q * y1; x = x1; y = y1; x1 = tx; y1 = ty; } return new(x); } [MethodImpl(256)] public static ModInt operator +(ModInt left, ModInt right) => CreateFast((left.Value + right.Value) % _mod.Mod); [MethodImpl(256)] public static ModInt operator +(ModInt self) => self; [MethodImpl(256)] public static ModInt operator -(ModInt left, ModInt right) => CreateFast((left.Value - right.Value + _mod.Mod) % _mod.Mod); [MethodImpl(256)] public static ModInt operator -(ModInt self) => CreateFast(_mod.Mod - self.Value); [MethodImpl(256)] public static ModInt operator *(ModInt left, ModInt right) => CreateFast(left.Value * right.Value % _mod.Mod); [MethodImpl(256)] public static ModInt operator /(ModInt left, ModInt right) { if (right.Value == 0L) { throw new DivideByZeroException(); } ModInt inv = right.Inv(); return CreateFast(left.Value * inv.Value % _mod.Mod); } public static ModInt operator %(ModInt left, ModInt right) { throw new NotImplementedException(); } public static bool operator <(ModInt left, ModInt right) { throw new NotImplementedException(); } public static bool operator >(ModInt left, ModInt right) { throw new NotImplementedException(); } public static bool operator <=(ModInt left, ModInt right) { throw new NotImplementedException(); } public static bool operator >=(ModInt left, ModInt right) { throw new NotImplementedException(); } [MethodImpl(256)] public static bool operator ==(ModInt left, ModInt right) => left.Value == right.Value; [MethodImpl(256)] public static bool operator !=(ModInt left, ModInt right) => !(left == right); [MethodImpl(256)] public static ModInt operator ++(ModInt self) => CreateFast((self.Value + 1) % _mod.Mod); [MethodImpl(256)] public static ModInt operator --(ModInt self) => CreateFast((self.Value - 1 + _mod.Mod) % _mod.Mod); [MethodImpl(256)] public bool Equals(ModInt other) => Value == other.Value; [MethodImpl(256)] public override bool Equals(object other) { if (other is ModInt m) { return this == m; } else return false; } [MethodImpl(256)] public override int GetHashCode() => Value.GetHashCode(); [MethodImpl(256)] public static implicit operator ModInt(long v) => new ModInt(v); [MethodImpl(256)] public static implicit operator ModInt(int v) => new ModInt(v); [MethodImpl(256)] public static implicit operator long(in ModInt m) => m.Value; [MethodImpl(256)] public static implicit operator int(in ModInt m) => (int)m.Value; public override string ToString() => Value.ToString(); public string ToString(string format, IFormatProvider provider) => Value.ToString(format, provider); #region INumberBase Implementation public static ModInt Abs(ModInt value) => value; public static bool IsCanonical(ModInt value) => true; public static bool IsComplexNumber(ModInt value) => false; public static bool IsFinite(ModInt value) => true; public static bool IsImaginaryNumber(ModInt value) => false; public static bool IsInfinity(ModInt value) => false; public static bool IsInteger(ModInt value) => true; public static bool IsNaN(ModInt value) => false; public static bool IsNegative(ModInt value) => false; public static bool IsPositive(ModInt value) => value.Value != 0L; public static bool IsRealNumber(ModInt value) => true; public static bool IsZero(ModInt value) => value.Value == 0L; public static bool IsEvenInteger(ModInt value) => (value.Value & 1) == 0; public static bool IsOddInteger(ModInt value) => (value.Value & 1) == 1; public static bool IsPositiveInfinity(ModInt value) => false; public static bool IsNegativeInfinity(ModInt value) => false; public static bool IsNormal(ModInt value) => false; public static bool IsSubnormal(ModInt value) => false; public static ModInt MaxMagnitude(ModInt x, ModInt y) { if (x.Value > y.Value) return x; else return y; } public static ModInt MaxMagnitudeNumber(ModInt x, ModInt y) => MaxMagnitude(x, y); public static ModInt MinMagnitude(ModInt x, ModInt y) { if (x.Value < y.Value) return x; else return y; } public static ModInt MinMagnitudeNumber(ModInt x, ModInt y) => MinMagnitude(x, y); public static ModInt CreateChecked(TOther value) where TOther : INumberBase => new(long.CreateChecked(value)); public static ModInt CreateSaturating(TOther value) where TOther : INumberBase => new(long.CreateSaturating(value)); public static ModInt CreateTruncating(TOther value) where TOther : INumberBase => new(long.CreateTruncating(value)); public static ModInt Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s.AsSpan(), style, provider); public static ModInt Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => new(long.Parse(s, style, provider)); #if NET8_0_OR_GREATER public static ModInt Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) => new(long.Parse(s, style, provider)); #endif public static ModInt Parse(string s, IFormatProvider provider) => new(long.Parse(s, provider)); public static ModInt Parse(ReadOnlySpan s, IFormatProvider provider) => new(long.Parse(s, provider)); #if NET8_0_OR_GREATER public bool TryFormat(Span dest, out int bytesWritten, ReadOnlySpan format, IFormatProvider provider) { return Value.TryFormat(dest, out bytesWritten, format, provider); } #endif public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) { return Value.TryFormat(destination, out charsWritten, format, provider); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out ModInt result) { if (long.TryParse(s, style, provider, out long inner)) { result = new(inner); return true; } else { result = Zero; return false; } } public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out ModInt result) { if (long.TryParse(s, style, provider, out long inner)) { result = new(inner); return true; } else { result = Zero; return false; } } public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out ModInt result) { if (long.TryParse(s, provider, out long inner)) { result = new(inner); return true; } else { result = Zero; return false; } } public static bool TryParse(string s, IFormatProvider provider, out ModInt result) { if (long.TryParse(s, provider, out long inner)) { result = new(inner); return true; } else { result = Zero; return false; } } public static bool TryConvertFromChecked(TOther value, out ModInt result) where TOther : INumberBase { throw new NotImplementedException(); } public static bool TryConvertFromSaturating(TOther value, out ModInt result) where TOther : INumberBase { throw new NotImplementedException(); } public static bool TryConvertFromTruncating(TOther value, [MaybeNullWhen(false)] out ModInt result) where TOther : INumberBase { throw new NotImplementedException(); } public static bool TryConvertToChecked(ModInt value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { throw new NotImplementedException(); } public static bool TryConvertToSaturating(ModInt value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { throw new NotImplementedException(); } public static bool TryConvertToTruncating(ModInt value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase { throw new NotImplementedException(); } #endregion #region INumber Implementation public int CompareTo(ModInt other) => Value.CompareTo(other.Value); public int CompareTo(object other) => Value.CompareTo(other); #endregion } /// /// 法を指定するやつ。 /// public interface IMod { public long Mod { get; } } public readonly struct Mod998244353 : IMod { public long Mod => 998244353L; } public readonly struct Mod1000000007 : IMod { public long Mod => 1000000007L; } public readonly struct Mod897581057 : IMod { public long Mod => 897581057; } public readonly struct Mod880803841 : IMod { public long Mod => 880803841; }