Problem:
Score : 800 points

Problem Statement

There is a pond with a rectangular shape.
The pond is divided into a grid with H rows and W columns of squares.
We will denote the square at the i-th row from the top and j-th column from the left by (i,\ j).

Some of the squares in the pond contains a lotus leaf floating on the water.
On one of those leaves, S, there is a frog trying to get to another leaf T.
The state of square (i,\ j) is given to you by a character a_{ij}, as follows:

. : A square without a leaf.

o : A square with a leaf floating on the water.

S : A square with the leaf S.

T : A square with the leaf T.

The frog will repeatedly perform the following action to get to the leaf T: "jump to a leaf that is in the same row or the same column as the leaf where the frog is currently located."

Snuke is trying to remove some of the leaves, other than S and T, so that the frog cannot get to the leaf T.
Determine whether this objective is achievable. If it is achievable, find the minimum necessary number of leaves to remove.

Constraints

2 ≤ H, W ≤ 100

a_{ij} is ., o, S or T.

There is exactly one S among a_{ij}.

There is exactly one T among a_{ij}.

Input

Input is given from Standard Input in the following format:

H W
a_{11} ... a_{1W}
:
a_{H1} ... a_{HW}

Output

If the objective is achievable, print the minimum necessary number of leaves to remove.
Otherwise, print -1 instead.

Sample Input 1

3 3
S.o
.o.
o.T

Sample Output 1

2

Remove the upper-right and lower-left leaves.

Sample Input 2

3 4
S...
.oo.
...T

Sample Output 2

0

Sample Input 3

4 3
.S.
.o.
.o.
.T.

Sample Output 3

-1

Sample Input 4

10 10
.o...o..o.
....o.....
....oo.oo.
..oooo..o.
....oo....
..o..o....
o..o....So
o....T....
....o.....
........oo

Sample Output 4

5

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Numerics;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace AtCoder
{
	public class Program
	{
		static void Main()
		{
			var cin = new Scanner();
			var (h, w) = cin.Int2();
			var a = new string[h];
			for (int i = 0; i < h; i++) {
				a[i] = cin.Next();
			}

			int n = h  + w + 2;
			(int i, int j) s = (0, 0);
			(int i, int j) t = (0, 0);
			for (int i = 0; i < h; i++) {
				for (int j = 0; j < w; j++) {
					if (a[i][j] == 'S') {
						s = (i, j);
					} else if (a[i][j] == 'T') {
						t = (i, j);
					} 
				}
			}

			if (s.i == t.i || s.j == t.j) {
				Console.WriteLine(-1);
				return;
			}

			int inf = h * w * 10;
			var edges = Helper.Array(n, x => new List<Flow.Edge>());
			for (int i = 0; i < h; i++) {
				for (int j = 0; j < w; j++) {
					if (a[i][j] == 'o') {
						Flow.AddEdge(edges, i, h + j, 1);
						Flow.AddEdge(edges, h + j, i, 1);

						if (i == s.i) {
							Flow.AddEdge(edges, h + w, i, inf);
						}

						if (j == s.j) {
							Flow.AddEdge(edges, h + w, h + j, inf);
						}

						if (i == t.i) {
							Flow.AddEdge(edges, i, h + w + 1, inf);
						}

						if (j == t.j) {
							Flow.AddEdge(edges, h + j, h + w + 1, inf);
						}
					}
				}
			}
			
			long ans = Flow.GetMaxFlowByDinic(n, edges, h + w, h + w + 1);
			Console.WriteLine(ans);
		}
	}

	public static class Flow
	{
		private static readonly long INF = long.MaxValue;

		public static void AddEdge(List<Flow.Edge>[] edges, int from, int to, long capacity, long cost = 1)
		{
			edges[from].Add(new Edge(to, capacity, cost, edges[to].Count));
			edges[to].Add(new Edge(from, 0, -1 * cost, edges[from].Count - 1));
		}

		public static long GetMaxFlowByFordFulkerson(int n, List<Flow.Edge>[] edges, int s, int t)
		{
			long flow = 0;
			while (true) {
				var done = new bool[n];
				long f = DfsOfFordFulkerson(edges, s, t, INF, done);
				if (f == 0 || f == INF) {
					break;
				}

				flow += f;
			}

			return flow;
		}

		private static long DfsOfFordFulkerson(List<Flow.Edge>[] edges, int s, int t, long f, bool[] done)
		{
			if (s == t) {
				return f;
			}

			done[s] = true;
			foreach (var edge in edges[s]) {
				if (done[edge.To] == false && edge.Capacity > 0) {
					long d = DfsOfFordFulkerson(edges, edge.To, t, Math.Min(f, edge.Capacity), done);
					if (d > 0) {
						edge.Capacity -= d;
						edges[edge.To][edge.ReverseEdgeIndex].Capacity += d;
						return d;
					}
				}
			}

			return 0;
		}

		public static long GetMaxFlowByDinic(int n, List<Flow.Edge>[] edges, int s, int t)
		{
			long flow = 0;
			while (true) {
				var distance = BfsOfDinic(n, edges, s);
				if (distance[t] < 0) {
					break;
				}

				var done = new int[n];
				while (true) {
					long f = DfsOfDinic(n, edges, s, t, INF, done, distance);
					if (f == 0 || f == INF) {
						break;
					}

					flow += f;
				}
			}

			return flow;
		}

		private static long[] BfsOfDinic(int n, List<Flow.Edge>[] edges, int s)
		{
			var d = new long[n];
			for (int i = 0; i < n; i++) {
				d[i] = -1;
			}

			var q = new Queue<int>();
			d[s] = 0;
			q.Enqueue(s);
			while (q.Count > 0) {
				int v = q.Dequeue();
				foreach (var edge in edges[v]) {
					if (edge.Capacity > 0 && d[edge.To] < 0) {
						d[edge.To] = d[v] + 1;
						q.Enqueue(edge.To);
					}
				}
			}

			return d;
		}

		private static long DfsOfDinic(
			int n, List<Flow.Edge>[] edges, int s, int t, long f, int[] done, long[] distance)
		{
			if (s == t) {
				return f;
			}

			for (; done[s] < edges[s].Count; done[s]++) {
				var edge = edges[s][done[s]];
				if (edge.Capacity > 0 && distance[s] < distance[edge.To]) {
					long d = DfsOfDinic(n, edges, edge.To, t, Math.Min(f, edge.Capacity), done, distance);
					if (d > 0) {
						edge.Capacity -= d;
						edges[edge.To][edge.ReverseEdgeIndex].Capacity += d;
						return d;
					}
				}
			}

			return 0;
		}

		public static long GetMinCostFlowByBellmanFord(int n, List<Flow.Edge>[] edges, int s, int t, long f)
		{
			var prevVertexes = new int[n];
			var prevEdgeIndexes = new int[n];

			long minCost = 0;
			while (f > 0) {
				var distances = new long[n];
				for (int i = 0; i < n; i++) {
					distances[i] = INF;
				}

				distances[s] = 0;
				for (int i = 0; i < n; i++) {
					bool changes = false;
					for (int v = 0; v < n; v++) {
						if (distances[v] == INF) {
							continue;
						}

						for (int j = 0; j < edges[v].Count; j++) {
							var edge = edges[v][j];
							long newDistance = distances[v] + edge.Cost;
							if (edge.Capacity > 0 && distances[edge.To] > newDistance) {
								distances[edge.To] = newDistance;
								prevVertexes[edge.To] = v;
								prevEdgeIndexes[edge.To] = j;
								changes = true;
							}
						}
					}

					if (i == n - 1 && changes) {
						return -1;
					}

					if (changes == false) {
						break;
					}
				}

				if (distances[t] == INF) {
					return -1;
				}

				long d = f;
				for (int v = t; v != s; v = prevVertexes[v]) {
					d = Math.Min(d, edges[prevVertexes[v]][prevEdgeIndexes[v]].Capacity);
				}

				f -= d;
				minCost += d * distances[t];
				for (int v = t; v != s; v = prevVertexes[v]) {
					edges[prevVertexes[v]][prevEdgeIndexes[v]].Capacity -= d;
					edges[v][edges[prevVertexes[v]][prevEdgeIndexes[v]].ReverseEdgeIndex].Capacity += d;
				}
			}

			return minCost;
		}

		public static long GetMinCostFlowByDijkstra(int n, List<Flow.Edge>[] edges, int s, int t, long f)
		{
			var potentials = new long[n];
			var prevVertexes = new int[n];
			var prevEdgeIndexes = new int[n];

			long minCost = 0;
			while (f > 0) {
				var distances = new long[n];
				for (int i = 0; i < n; i++) {
					distances[i] = INF;
				}

				distances[s] = 0;
				var pq = new PriorityQueueP<long, int>(true);
				pq.Enqueue(0, s);
				while (pq.Count > 0) {
					var current = pq.Dequeue();
					int v = current.Item;
					if (distances[v] < current.Priority) {
						continue;
					}

					for (int j = 0; j < edges[v].Count; j++) {
						var edge = edges[v][j];
						long newDistance = distances[v] + edge.Cost + potentials[v] - potentials[edge.To];
						if (edge.Capacity > 0 && distances[edge.To] > newDistance) {
							distances[edge.To] = newDistance;
							prevVertexes[edge.To] = v;
							prevEdgeIndexes[edge.To] = j;
							pq.Enqueue(newDistance, edge.To);
						}
					}
				}

				if (distances[t] == INF) {
					return -1;
				}

				for (int v = 0; v < n; v++) {
					potentials[v] += distances[v];
				}

				long d = f;
				for (int v = t; v != s; v = prevVertexes[v]) {
					d = Math.Min(d, edges[prevVertexes[v]][prevEdgeIndexes[v]].Capacity);
				}

				f -= d;
				minCost += d * potentials[t];
				for (int v = t; v != s; v = prevVertexes[v]) {
					edges[prevVertexes[v]][prevEdgeIndexes[v]].Capacity -= d;
					edges[v][edges[prevVertexes[v]][prevEdgeIndexes[v]].ReverseEdgeIndex].Capacity += d;
				}
			}

			return minCost;
		}

		public class Edge
		{
			public int To { get; set; }
			public int ReverseEdgeIndex { get; set; }
			public long Capacity { get; set; }
			public long Cost { get; set; }
			public Edge(int to, long capacity, long cost, int reverse)
			{
				To = to;
				Capacity = capacity;
				Cost = cost;
				ReverseEdgeIndex = reverse;
			}
		}
	}

	public struct PriorityPair<TPriority, TItem> : IComparable<PriorityPair<TPriority, TItem>>
			where TPriority : IComparable<TPriority>
	{
		public TPriority Priority { get; set; }
		public TItem Item { get; set; }

		public PriorityPair(TPriority priority, TItem item)
		{
			Priority = priority;
			Item = item;
		}

		public int CompareTo(PriorityPair<TPriority, TItem> target)
			=> Priority.CompareTo(target.Priority);

		public override string ToString()
			=> $"{Priority} {Item}";
	}

	public class PriorityQueueP<TPriority, TItem> where TPriority : IComparable<TPriority>
	{
		private readonly int reverseFactor_;
		private PriorityPair<TPriority, TItem>[] heap_;

		public PriorityPair<TPriority, TItem> Top => heap_[0];
		public int Count { get; private set; } = 0;

		public PriorityQueueP(bool reverses = false)
			: this(1024, reverses) { }

		public PriorityQueueP(
			int capacity,
			bool reverses = false)
		{
			if (capacity == 0) {
				capacity = 1024;
			}

			heap_ = new PriorityPair<TPriority, TItem>[capacity > 0 ? capacity : 1];
			reverseFactor_ = reverses ? -1 : 1;
		}

		public void Enqueue(TPriority priority, TItem item)
		{
			var pair = new PriorityPair<TPriority, TItem>(priority, item);
			if (heap_.Length == Count) {
				Extend(heap_.Length * 2);
			}

			heap_[Count] = pair;
			++Count;

			int c = Count - 1;
			while (c > 0) {
				int p = (c - 1) >> 1;
				if (Compare(heap_[p].Priority, priority) < 0) {
					heap_[c] = heap_[p];
					c = p;
				} else {
					break;
				}
			}

			heap_[c] = pair;
		}

		public PriorityPair<TPriority, TItem> Dequeue()
		{
			PriorityPair<TPriority, TItem> ret = heap_[0];
			int n = Count - 1;

			var item = heap_[n];
			int p = 0;
			int c = (p << 1) + 1;
			while (c < n) {
				if (c != n - 1 && Compare(heap_[c + 1].Priority, heap_[c].Priority) > 0) {
					++c;
				}

				if (Compare(item.Priority, heap_[c].Priority) < 0) {
					heap_[p] = heap_[c];
					p = c;
					c = (p << 1) + 1;
				} else {
					break;
				}
			}

			heap_[p] = item;
			Count--;

			return ret;
		}

		public void Clear() => Count = 0;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private int Compare(TPriority x, TPriority y)
			=> x.CompareTo(y) * reverseFactor_;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void Extend(int newSize)
		{
			var newHeap = new PriorityPair<TPriority, TItem>[newSize];
			heap_.CopyTo(newHeap, 0);
			heap_ = newHeap;
		}
	}

	public class HashMap<TKey, TValue> : Dictionary<TKey, TValue>
	{
		private readonly Func<TKey, TValue> initialzier_;
		public HashMap(Func<TKey, TValue> initialzier)
			: base()
		{
			initialzier_ = initialzier;
		}

		public HashMap(Func<TKey, TValue> initialzier, int capacity)
			: base(capacity)
		{
			initialzier_ = initialzier;
		}

		new public TValue this[TKey key]
		{
			get
			{
				if (ContainsKey(key) == false) {
					base[key] = initialzier_(key);
				}

				return base[key];
			}

			set { base[key] = value; }
		}
	}

	public struct ModInt
	{
		public const long P = 1000000007;
		//public const long P = 998244353;

		public static ModInt Inverse(ModInt value) => Pow(value, P - 2);
		public static ModInt Pow(ModInt value, long k) => Pow(value.value_, k);
		public static ModInt Pow(long value, long k)
		{
			long ret = 1;
			for (k %= P - 1; k > 0; k >>= 1, value = value * value % P) {
				if ((k & 1) == 1) {
					ret = ret * value % P;
				}
			}
			return new ModInt(ret);
		}

		private long value_;

		public ModInt(long value)
			=> value_ = value;
		public ModInt(long value, bool mods)
		{
			if (mods) {
				value %= P;
				if (value < 0) {
					value += P;
				}
			}

			value_ = value;
		}

		public static ModInt operator +(ModInt lhs, ModInt rhs)
		{
			lhs.value_ = (lhs.value_ + rhs.value_) % P;
			return lhs;
		}
		public static ModInt operator +(long lhs, ModInt rhs)
		{
			rhs.value_ = (lhs + rhs.value_) % P;
			return rhs;
		}
		public static ModInt operator +(ModInt lhs, long rhs)
		{
			lhs.value_ = (lhs.value_ + rhs) % P;
			return lhs;
		}

		public static ModInt operator -(ModInt lhs, ModInt rhs)
		{
			lhs.value_ = (P + lhs.value_ - rhs.value_) % P;
			return lhs;
		}
		public static ModInt operator -(long lhs, ModInt rhs)
		{
			rhs.value_ = (P + lhs - rhs.value_) % P;
			return rhs;
		}
		public static ModInt operator -(ModInt lhs, long rhs)
		{
			lhs.value_ = (P + lhs.value_ - rhs) % P;
			return lhs;
		}

		public static ModInt operator *(ModInt lhs, ModInt rhs)
		{
			lhs.value_ = lhs.value_ * rhs.value_ % P;
			return lhs;
		}
		public static ModInt operator *(long lhs, ModInt rhs)
		{
			rhs.value_ = lhs * rhs.value_ % P;
			return rhs;
		}
		public static ModInt operator *(ModInt lhs, long rhs)
		{
			lhs.value_ = lhs.value_ * rhs % P;
			return lhs;
		}

		public static ModInt operator /(ModInt lhs, ModInt rhs)
		{
			long exp = P - 2;
			while (exp > 0) {
				if (exp % 2 > 0) {
					lhs *= rhs;
				}

				rhs *= rhs;
				exp /= 2;
			}

			return lhs;
		}

		public static implicit operator ModInt(long n) => new ModInt(n, true);
		public long ToLong() => value_;
		public override string ToString() => value_.ToString();
	}

	public static class Helper
	{
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void UpdateMin<T>(ref T target, T value) where T : IComparable<T>
			=> target = target.CompareTo(value) > 0 ? value : target;
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void UpdateMin<T>(ref T target, T value, Action<T> onUpdated) where T : IComparable<T>
		{
			if (target.CompareTo(value) > 0) {
				target = value;
				onUpdated(value);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void UpdateMax<T>(ref T target, T value) where T : IComparable<T>
			=> target = target.CompareTo(value) < 0 ? value : target;
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void UpdateMax<T>(ref T target, T value, Action<T> onUpdated) where T : IComparable<T>
		{
			if (target.CompareTo(value) < 0) {
				target = value;
				onUpdated(value);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static T[] Array<T>(int n, Func<int, T> init)
			=> Enumerable.Range(0, n).Select(x => init(x)).ToArray();
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static List<T> List<T>(int n, Func<int, T> init)
			=> Enumerable.Range(0, n).Select(x => init(x)).ToList();

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static T[,] Array2<T>(int n, int m, T init)
			where T : struct
		{
			var array = new T[n, m];
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {
					array[i, j] = init;
				}
			}

			return array;
		}
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static T[,] Array2<T>(int n, int m, Func<int, int, T> initializer)
		{
			var array = new T[n, m];
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {
					array[i, j] = initializer(i, j);
				}
			}

			return array;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static T[,,] Array3<T>(int n1, int n2, int n3, T init) 
			where T : struct
		{
			var array = new T[n1, n2, n3];
			for (int i1 = 0; i1 < n1; i1++) {
				for (int i2 = 0; i2 < n2; i2++) {
					for (int i3 = 0; i3 < n3; i3++) {
						array[i1, i2, i3] = init;
					}
				}
			}

			return array;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static T[,,,] Array4<T>(int n1, int n2, int n3, int n4, T init) 
			where T : struct
		{
			var array = new T[n1, n2, n3, n4];
			for (int i1 = 0; i1 < n1; i1++) {
				for (int i2 = 0; i2 < n2; i2++) {
					for (int i3 = 0; i3 < n3; i3++) {
						for (int i4 = 0; i4 < n4; i4++) {
							array[i1, i2, i3, i4] = init;
						}
					}
				}
			}

			return array;
		}

		private static readonly int[] delta4_ = { 1, 0, -1, 0, 1 };
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void DoAt4(int i, int j, int imax, int jmax, Action<int, int> action)
		{
			for (int dn = 0; dn < 4; dn++) {
				int d4i = i + delta4_[dn];
				int d4j = j + delta4_[dn + 1];
				if ((uint)d4i < (uint)imax && (uint)d4j < (uint)jmax) {
					action(d4i, d4j);
				}
			}
		}

		private static readonly int[] delta8_ = { 1, 0, -1, 0, 1, 1, -1, -1, 1 };
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void DoAt8(int i, int j, int imax, int jmax, Action<int, int> action)
		{
			for (int dn = 0; dn < 8; dn++) {
				int d8i = i + delta8_[dn];
				int d8j = j + delta8_[dn + 1];
				if ((uint)d8i < (uint)imax && (uint)d8j < (uint)jmax) {
					action(d8i, d8j);
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void ForEachSubBit(int bit, Action<int> action)
		{
			for (int sub = bit; sub >= 0; sub--) {
				sub &= bit;
				action(sub);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static string Reverse(string src)
		{
			var chars = src.ToCharArray();
			for (int i = 0, j = chars.Length - 1; i < j; i++, j--) {
				var tmp = chars[i];
				chars[i] = chars[j];
				chars[j] = tmp;
			}
			return new string(chars);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static string Join<T>(this IEnumerable<T> values, string separator = "")
			=> string.Join(separator, values);
	}

	public class Scanner
	{
		private readonly char[] delimiter_ = new char[] { ' ' };
		private readonly string filePath_;
		private readonly Func<string> reader_;
		private string[] buf_;
		private int index_;

		public Scanner(string file = "")
		{
			if (string.IsNullOrWhiteSpace(file)) {
				reader_ = Console.ReadLine;
			} else {
				filePath_ = file;
				var fs = new StreamReader(file);
				reader_ = fs.ReadLine;
			}
			buf_ = new string[0];
			index_ = 0;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public string NextLine() => reader_();
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public string Next()
		{
			if (index_ < buf_.Length) {
				return buf_[index_++];
			}

			string st = reader_();
			while (st == "") {
				st = reader_();
			}

			buf_ = st.Split(delimiter_, StringSplitOptions.RemoveEmptyEntries);
			if (buf_.Length == 0) {
				return Next();
			}

			index_ = 0;
			return buf_[index_++];
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public int Int() => int.Parse(Next());
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public int Int(int offset) => int.Parse(Next()) + offset;
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (int, int) Int2(int offset = 0) 
			=> (Int(offset), Int(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (int, int, int) Int3(int offset = 0) 
			=> (Int(offset), Int(offset), Int(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (int, int, int, int) Int4(int offset = 0) 
			=> (Int(offset), Int(offset), Int(offset), Int(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public int[] ArrayInt(int length, int offset = 0)
		{
			int[] Array = new int[length];
			for (int i = 0; i < length; i++) {
				Array[i] = Int(offset);
			}
			return Array;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public long Long() => long.Parse(Next());
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public long Long(long offset) => long.Parse(Next()) + offset;
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (long, long) Long2(long offset = 0)
			=> (Long(offset), Long(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (long, long, long) Long3(long offset = 0) 
			=> (Long(offset), Long(offset), Long(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (long, long, long, long) Long4(long offset = 0) 
			=> (Long(offset), Long(offset), Long(offset), Long(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public long[] ArrayLong(int length, long offset = 0)
		{
			long[] Array = new long[length];
			for (int i = 0; i < length; i++) {
				Array[i] = Long(offset);
			}
			return Array;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public double Double() => double.Parse(Next());
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public double Double(double offset) => double.Parse(Next()) + offset;
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (double, double) Double2(double offset = 0) 
			=> (Double(offset), Double(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (double, double, double) Double3(double offset = 0)
			=> (Double(offset), Double(offset), Double(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public (double, double, double, double) Double4(double offset = 0)
			=> (Double(offset), Double(offset), Double(offset), Double(offset));
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public double[] ArrayDouble(int length, double offset = 0)
		{
			double[] Array = new double[length];
			for (int i = 0; i < length; i++) {
				Array[i] = Double(offset);
			}
			return Array;
		}

		public void Save(string text)
		{
			if (string.IsNullOrWhiteSpace(filePath_)) {
				return;
			}

			File.WriteAllText(filePath_ + "_output.txt", text);
		}
	}
}