Decompiled source of SODStockMarket v2.0.6

SOD.StockMarket.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using AssetBundleLoader;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using Bogus;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using SOD.Common;
using SOD.Common.BepInEx;
using SOD.Common.BepInEx.Configuration;
using SOD.Common.Custom;
using SOD.Common.Extensions;
using SOD.Common.Helpers;
using SOD.StockMarket.Implementation;
using SOD.StockMarket.Implementation.Cruncher;
using SOD.StockMarket.Implementation.Cruncher.Content;
using SOD.StockMarket.Implementation.Cruncher.News;
using SOD.StockMarket.Implementation.DataConversion;
using SOD.StockMarket.Implementation.DataConversion.Converters;
using SOD.StockMarket.Implementation.Stocks;
using SOD.StockMarket.Implementation.Trade;
using SOD.StockMarket.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using UniverseLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SOD.StockMarket")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+419fa897ea2962db3054776201f942af074b9596")]
[assembly: AssemblyProduct("SOD.StockMarket")]
[assembly: AssemblyTitle("SOD.StockMarket")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
}
namespace SOD.StockMarket
{
	internal class Constants
	{
		internal const bool IsDebugEnabled = false;

		internal const string StockDataSaveFormat = "bin";

		internal const int OpeningHour = 9;

		internal const int ClosingHour = 16;

		internal const string DaysClosed = "Saturday,Sunday";

		internal const double StockTrendChancePercentage = 3.0;

		internal const int MaxTrends = 10;

		internal const int MaxHoursTrendsCanPersist = 12;

		internal const int MinHoursTrendsMustPersist = 1;

		internal const double PriceFluctuationPercentage = 0.35;

		internal const int DaysToKeepStockHistoricalData = 31;

		internal const double PastHistoricalDataVolatility = 15.0;

		internal const bool RunSimulation = false;

		internal const int SimulationDays = 365;

		internal const int MinimumStocksInMarket = 50;
	}
	public interface IPluginBindings : IDebugBindings, IMarketBindings, IEconomyBindings
	{
	}
	public interface IDebugBindings
	{
		[Binding(false, "Enables debug mode which prints several useful information into the console.", "Debugging.EnableDebugMode")]
		bool IsDebugEnabled { get; set; }

		[Binding("bin", "The save format for the stock data. Options: \"csv\", \"bin\")", "Debugging.StockDataSaveFormat")]
		string StockDataSaveFormat { get; set; }

		[Binding(false, "If this option is enabled, it will run a simulation after initial economy load and export it as a csv.", "Debugging.RunSimulation")]
		bool RunSimulation { get; set; }

		[Binding(365, "The total amount of days to simulate for (only if RunSimulation is enabled).", "Debugging.SimulationDays")]
		int SimulationDays { get; set; }
	}
	public interface IMarketBindings
	{
		[Binding(9, "The hour the stock market opens everyday. (24h clock)", "StockMarket.OpeningHour")]
		int OpeningHour { get; set; }

		[Binding(16, "The hour the stock market closes everyday. (24h clock)", "StockMarket.ClosingHour")]
		int ClosingHour { get; set; }

		[Binding("Saturday,Sunday", "The days the stock market is closed.", "StockMarket.DaysClosed")]
		string DaysClosed { get; set; }
	}
	public interface IEconomyBindings
	{
		[Binding(3.0, "The percentage change a stock has to start a trend. (0-100)", "StockMarket.Economy.StockTrendChancePercentage")]
		double StockTrendChancePercentage { get; set; }

		[Binding(10, "The maximum amount of trends that can be ongoing at once. (-1 for unlimited)", "StockMarket.Economy.MaxTrends")]
		int MaxTrends { get; set; }

		[Binding(12, "The maximum amount of hours a trend can persist until its completed. (MIN 1)", "StockMarket.Economy.MaxHoursTrendsCanPersist")]
		int MaxHoursTrendsCanPersist { get; set; }

		[Binding(1, "The minimum amount of hours a trend must persist until its completed. (MIN 1)", "StockMarket.Economy.MinHoursTrendsMustPersist")]
		int MinHoursTrendsMustPersist { get; set; }

		[Binding(0.35, "The base price fluctuation percentage of stocks. (based on stock volatility)", "StockMarket.Economy.PriceFluctuationPercentage")]
		double PriceFluctuationPercentage { get; set; }

		[Binding(31, "The amount of days the historical data should be kept per stock.", "StockMarket.Economy.DaysToKeepStockHistoricalData")]
		int DaysToKeepStockHistoricalData { get; set; }

		[Binding(15.0, "The base percentage of volatility the market has been for the past [DaysToKeepStockHistoricalData] on market initialization. (MIN 1.0)", "Stockmarket.Economy.PastHistoricalDataVolatility")]
		double PastHistoricalDataVolatility { get; set; }

		[Binding(50, "The minimum amount of stocks that should be in the market on generation.", "StockMarket.Economy.MinimumStocksInMarket")]
		int MinimumStocksInMarket { get; set; }
	}
	[BepInPlugin("Venomaus.SOD.StockMarket", "StockMarket", "2.0.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : PluginController<Plugin, IPluginBindings>
	{
		public const string PLUGIN_GUID = "Venomaus.SOD.StockMarket";

		public const string PLUGIN_NAME = "StockMarket";

		public const string PLUGIN_VERSION = "2.0.6";

		internal Market Market { get; private set; }

		public override void Load()
		{
			ClassInjector.RegisterTypeInIl2Cpp<StockMarketAppContent>();
			PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Plugin registered custom types into IL2CPP domain.");
			Market = new Market();
			PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Plugin initialized stock market hooks.");
			base.Harmony.PatchAll(Assembly.GetExecutingAssembly());
			PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Plugin is patched.");
		}

		public override void OnConfigureBindings()
		{
			base.OnConfigureBindings();
			ValidateBindingValues();
		}

		private void ValidateBindingValues()
		{
			if (base.Config.MaxHoursTrendsCanPersist < 1)
			{
				base.Config.MaxHoursTrendsCanPersist = 12;
			}
			if (base.Config.MinHoursTrendsMustPersist < 1)
			{
				base.Config.MinHoursTrendsMustPersist = 1;
			}
			double stockTrendChancePercentage = base.Config.StockTrendChancePercentage;
			if (stockTrendChancePercentage < 0.0 || stockTrendChancePercentage > 100.0)
			{
				base.Config.StockTrendChancePercentage = 3.0;
			}
			if (base.Config.MaxTrends < -1)
			{
				base.Config.MaxTrends = 10;
			}
			if (base.Config.PastHistoricalDataVolatility < 1.0)
			{
				base.Config.PastHistoricalDataVolatility = 15.0;
			}
			if (!Enum.TryParse<DataSaveFormat>(base.Config.StockDataSaveFormat.Trim(), ignoreCase: true, out var _))
			{
				base.Config.StockDataSaveFormat = "bin";
			}
		}
	}
}
namespace SOD.StockMarket.Patches
{
	internal class CitizenCreatorPatches
	{
		[HarmonyPatch(typeof(CitizenCreator), "Populate")]
		internal class CitizenCreator_Populate
		{
			internal static bool Init;

			[HarmonyPostfix]
			internal static void Postfix()
			{
				if (!Init)
				{
					Init = true;
					PluginController<Plugin, IPluginBindings>.Instance.Market.PostStocksInitialization(typeof(CitizenCreator));
				}
			}
		}
	}
	internal class CityConstructorPatches
	{
		[HarmonyPatch(typeof(CityConstructor), "Finalized")]
		internal class CityConstructor_Finalized
		{
			internal static bool Init;

			[HarmonyPostfix]
			internal static void Postfix()
			{
				if (!Init)
				{
					Init = true;
					PluginController<Plugin, IPluginBindings>.Instance.Market.PostStocksInitialization(typeof(CityConstructor));
				}
			}
		}
	}
	internal class CompanyPatches
	{
		[HarmonyPatch(typeof(Company), "Setup")]
		internal class Company_Setup
		{
			internal static bool ShownInitializingMessage;

			[HarmonyPostfix]
			internal static void Postfix(Company __instance)
			{
				if (!PluginController<Plugin, IPluginBindings>.Instance.Market.Initialized && (!((SoCustomComparison)(object)__instance.preset != (SoCustomComparison)null) || !__instance.preset.isSelfEmployed) && !__instance.preset.isIllegal && __instance.preset.publicFacing)
				{
					if (!ShownInitializingMessage)
					{
						PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Initializing stock market data.");
						ShownInitializingMessage = true;
					}
					PluginController<Plugin, IPluginBindings>.Instance.Market.InitStock(new Stock(__instance));
				}
			}
		}
	}
	internal class InteriorCreatorPatches
	{
		[HarmonyPatch(typeof(InteriorCreator), "GenChunk")]
		internal class InteriorCreator_GenChunk
		{
			internal static bool Init;

			[HarmonyPostfix]
			internal static void Postfix()
			{
				if (!Init)
				{
					Init = true;
					PluginController<Plugin, IPluginBindings>.Instance.Market.PostStocksInitialization(typeof(InteriorCreator));
				}
			}
		}
	}
	internal class MainMenuControllerPatches
	{
		[HarmonyPatch(typeof(MainMenuController), "Start")]
		internal class MainMenuController_Start
		{
			private static bool _init;

			[HarmonyPostfix]
			internal static void Postfix()
			{
				if (!_init)
				{
					_init = true;
					LoadStockMarketBundle();
				}
			}

			private static void LoadStockMarketBundle()
			{
				string path = "stockmarketbundle";
				CruncherAppPreset obj = BundleLoader.LoadBundle(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), path), false, true).LoadAsset<CruncherAppPreset>("StockMarketPreset");
				obj.appContent[0].AddComponent<StockMarketAppContent>();
				InsertAppToCrunchers(obj);
			}

			private static void InsertAppToCrunchers(CruncherAppPreset preset)
			{
				foreach (InteractablePreset item in ((IEnumerable<InteractablePreset>)Resources.FindObjectsOfTypeAll<InteractablePreset>()).Where((InteractablePreset preset) => ((Object)preset).name.Contains("Cruncher")))
				{
					item.additionalApps.Insert(item.additionalApps.Count - 2, preset);
				}
			}
		}
	}
}
namespace SOD.StockMarket.Implementation
{
	internal class Market : IStocksContainer
	{
		private readonly List<Stock> _stocks;

		private bool _interiorCreatorFinished;

		private bool _citizenCreatorFinished;

		private bool _cityConstructorFinalized;

		private readonly bool _simulation;

		internal TimeData? SimulationTime;

		private bool _isLoading;

		private string _afterPreModPath;

		public IReadOnlyList<Stock> Stocks => _stocks;

		internal bool Initialized { get; private set; }

		private static int OpeningHour => ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.OpeningHour;

		private static int ClosingHour => ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ClosingHour;

		internal TradeController TradeController { get; private set; }

		internal event EventHandler<EventArgs> OnCalculate;

		internal event EventHandler<EventArgs> OnInitialized;

		internal Market()
		{
			_stocks = new List<Stock>();
			TradeController = new TradeController(this);
			Lib.SaveGame.OnBeforeNewGame += OnBeforeNewGame;
			Lib.SaveGame.OnBeforeLoad += OnFileLoad;
			Lib.SaveGame.OnBeforeSave += OnFileSave;
			Lib.SaveGame.OnBeforeDelete += OnFileDelete;
			Lib.Time.OnTimeInitialized += InitDdsRecords;
			Lib.Time.OnMinuteChanged += OnMinuteChanged;
			Lib.Time.OnHourChanged += OnHourChanged;
			if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.RunSimulation)
			{
				return;
			}
			OnInitialized += delegate
			{
				if (!Lib.Time.IsInitialized)
				{
					Lib.Time.OnTimeInitialized += OnTimeInit;
				}
				else
				{
					Simulate(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.SimulationDays);
				}
			};
		}

		private void InitDdsRecords(object sender, TimeChangedArgs e)
		{
			Lib.DdsStrings["computer", "stockmarketpreset"] = "Stock Market";
			Lib.Time.OnTimeInitialized -= InitDdsRecords;
		}

		private void OnTimeInit(object sender, TimeChangedArgs args)
		{
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.RunSimulation)
			{
				Simulate(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.SimulationDays);
			}
			Lib.Time.OnTimeInitialized -= OnTimeInit;
		}

		private Market(Market market)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			_stocks = new List<Stock>();
			foreach (Stock stock in market.Stocks)
			{
				_stocks.Add(new Stock(stock));
			}
			TradeController = new TradeController(this);
			_simulation = true;
			SimulationTime = new TimeData(1979, 1, 1, ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.OpeningHour, 0);
			Initialized = true;
		}

		internal void Simulate(int days)
		{
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			Market market = new Market(this);
			TradeController tradeController = market.TradeController;
			int num = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.ClosingHour - ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.OpeningHour;
			int openingHour = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.OpeningHour;
			for (int i = 0; i < days; i++)
			{
				TimeData value;
				for (int j = 0; j < num; j++)
				{
					for (int k = 0; k < 59; k++)
					{
						value = market.SimulationTime.Value;
						int year = ((TimeData)(ref value)).Year;
						value = market.SimulationTime.Value;
						int month = ((TimeData)(ref value)).Month;
						value = market.SimulationTime.Value;
						int day = ((TimeData)(ref value)).Day;
						value = market.SimulationTime.Value;
						market.SimulationTime = new TimeData(year, month, day, ((TimeData)(ref value)).Hour, k);
						market.OnMinuteChanged(this, null);
					}
					value = market.SimulationTime.Value;
					int year2 = ((TimeData)(ref value)).Year;
					value = market.SimulationTime.Value;
					int month2 = ((TimeData)(ref value)).Month;
					value = market.SimulationTime.Value;
					int day2 = ((TimeData)(ref value)).Day;
					value = market.SimulationTime.Value;
					market.SimulationTime = new TimeData(year2, month2, day2, ((TimeData)(ref value)).Hour + 1, 0);
					market.OnHourChanged(this, null);
				}
				value = market.SimulationTime.Value;
				int year3 = ((TimeData)(ref value)).Year;
				value = market.SimulationTime.Value;
				int month3 = ((TimeData)(ref value)).Month;
				value = market.SimulationTime.Value;
				market.SimulationTime = new TimeData(year3, month3, ((TimeData)(ref value)).Day, openingHour, 0);
				value = market.SimulationTime.Value;
				market.SimulationTime = ((TimeData)(ref value)).AddDays(1);
			}
			StockDataIO.Export(market, tradeController, Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), "Simulation.csv"), this);
		}

		private void OnBeforeNewGame(object sender, EventArgs e)
		{
			_stocks.Clear();
			TradeController.Reset();
			NewsGenerator.Clear();
			CitizenCreatorPatches.CitizenCreator_Populate.Init = false;
			CityConstructorPatches.CityConstructor_Finalized.Init = false;
			CompanyPatches.Company_Setup.ShownInitializingMessage = false;
			InteriorCreatorPatches.InteriorCreator_GenChunk.Init = false;
			_interiorCreatorFinished = false;
			_cityConstructorFinalized = false;
			_citizenCreatorFinished = false;
			Initialized = false;
			Lib.Time.OnTimeInitialized -= InitializeMarket;
			Lib.Time.OnTimeInitialized += InitializeMarket;
		}

		internal void PostStocksInitialization(Type type)
		{
			if (type == typeof(CitizenCreator))
			{
				_citizenCreatorFinished = true;
			}
			else if (type == typeof(InteriorCreator))
			{
				_interiorCreatorFinished = true;
			}
			else if (type == typeof(CityConstructor))
			{
				_cityConstructorFinalized = true;
			}
			else if (type == typeof(StockDataIO))
			{
				_citizenCreatorFinished = true;
				_interiorCreatorFinished = true;
				_cityConstructorFinalized = true;
				Initialized = true;
				_isLoading = false;
				this.OnInitialized?.Invoke(this, EventArgs.Empty);
				return;
			}
			if (_isLoading || Initialized || !_citizenCreatorFinished || !_interiorCreatorFinished || !_cityConstructorFinalized)
			{
				return;
			}
			MathHelper.Init(TypeExtensions.GetFnvHashCode(CityData.Instance.seed));
			(CompanyStockData, decimal?)[] stocks = CustomStocks.Stocks;
			for (int i = 0; i < stocks.Length; i++)
			{
				var (companyData, basePrice) = stocks[i];
				InitStock(new Stock(companyData, basePrice));
			}
			if (_stocks.Count < ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.MinimumStocksInMarket)
			{
				int num = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.MinimumStocksInMarket - _stocks.Count;
				for (int j = 0; j < num; j++)
				{
					Stock stock = new Stock(new CompanyStockData(StockNameGenerator.GenerateStockName(), Math.Round(MathHelper.Random.NextDouble(0.15, 0.85), 2)));
					InitStock(stock);
				}
			}
			foreach (Stock stock2 in _stocks)
			{
				stock2.Initialize();
			}
			StockSymbolGenerator.Clear();
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled)
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)("Stocks created: " + _stocks.Count));
			}
			PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Stock market initialized.");
			Initialized = true;
		}

		internal void InitStock(Stock stock)
		{
			if (!Initialized)
			{
				_stocks.Add(stock);
			}
		}

		private void InitializeMarket(object sender, TimeChangedArgs e)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Expected O, but got Unknown
			Lib.Time.OnTimeInitialized -= InitializeMarket;
			if (!_simulation)
			{
				TradeController.CreatePortfolioHistoricalDataEntry();
			}
			int num = 0;
			TimeData currentDate = Lib.Time.CurrentDate;
			int daysToKeepStockHistoricalData = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.DaysToKeepStockHistoricalData;
			float num2 = (float)((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PastHistoricalDataVolatility;
			bool flag = default(bool);
			foreach (Stock stock in _stocks)
			{
				StockData stockData = null;
				for (int num3 = daysToKeepStockHistoricalData + 1; num3 > 0; num3--)
				{
					TimeData date = ((TimeData)(ref currentDate)).AddDays(-num3);
					StockData stockData2 = new StockData
					{
						Date = date,
						Open = (stockData?.Close ?? stock.Price)
					};
					float num4 = (0f - num2) * (float)stock.Volatility;
					float num5 = num2 * (float)stock.Volatility;
					_ = stock.Volatility;
					stockData2.Close = Math.Round(stockData2.Open + stockData2.Open / 100m * (decimal)MathHelper.Random.NextDouble((double)num4, (double)num5), 2);
					decimal? close = stockData2.Close;
					if ((close.GetValueOrDefault() <= default(decimal)) & close.HasValue)
					{
						stockData2.Close = 0.01m;
					}
					stockData2.Low = Math.Round(stockData2.Close.Value + stockData2.Close.Value / 100m * (decimal)MathHelper.Random.NextDouble((double)num4, 0.0), 2);
					if (stockData2.Low <= 0m)
					{
						stockData2.Low = 0.01m;
					}
					stockData2.High = Math.Round(stockData2.Close.Value + stockData2.Close.Value / 100m * (decimal)MathHelper.Random.NextDouble(0.0, (double)num5), 2);
					if (stockData2.High <= 0m)
					{
						stockData2.High = 0.01m;
					}
					stock.CreateHistoricalData(stockData2);
					stockData = stockData2;
					num++;
				}
				if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled)
				{
					continue;
				}
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(65, 7, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Stock(");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(stock.Symbol);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(stock.Name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Volatility (");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(stock.Volatility);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") | ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Close (");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(Math.Round(stock.HistoricalData.Average((StockData a) => a.Close.Value), 2));
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") | ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Open (");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(Math.Round(stock.HistoricalData.Average((StockData a) => a.Open), 2));
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") | ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("High (");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(Math.Round(stock.HistoricalData.Average((StockData a) => a.High), 2));
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") | ");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Low (");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(Math.Round(stock.HistoricalData.Average((StockData a) => a.Low), 2));
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
				}
				log.LogInfo(val);
			}
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled)
			{
				ManualLogSource log2 = PluginController<Plugin, IPluginBindings>.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(37, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Initialized ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" historical data entries.");
				}
				log2.LogInfo(val);
			}
			GenerateTrends();
			this.OnInitialized?.Invoke(this, EventArgs.Empty);
		}

		private void OnMinuteChanged(object sender, TimeChangedArgs args)
		{
			if (IsOpen())
			{
				NewsGenerator.TickToBeReleased();
				if (args == null || !args.IsHourChanged)
				{
					Calculate();
				}
			}
		}

		private void OnHourChanged(object sender, TimeChangedArgs args)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			TimeData val = (TimeData)(((??)SimulationTime) ?? Lib.Time.CurrentDateTime);
			if (((TimeData)(ref val)).Hour == OpeningHour)
			{
				OnOpen();
			}
			else if (((TimeData)(ref val)).Hour == ClosingHour)
			{
				OnClose();
			}
			if (!IsOpen())
			{
				return;
			}
			Calculate();
			GenerateTrends();
			NewsGenerator.RemoveOutdatedArticles();
			if (!((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled || _simulation)
			{
				return;
			}
			ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(21, 0, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("- New stock updates -");
			}
			log.LogInfo(val2);
			foreach (Stock item in _stocks.OrderBy((Stock a) => a.Id))
			{
				ManualLogSource log2 = PluginController<Plugin, IPluginBindings>.Log;
				val2 = new BepInExInfoLogInterpolatedStringHandler(23, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Stock: \"(");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(item.Symbol);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(") ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(item.Name);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\" | Price: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<decimal>(item.Price);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
				}
				log2.LogInfo(val2);
			}
			ManualLogSource log3 = PluginController<Plugin, IPluginBindings>.Log;
			val2 = new BepInExInfoLogInterpolatedStringHandler(17, 0, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("- End of Stocks -");
			}
			log3.LogInfo(val2);
		}

		internal bool IsOpen()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if (!_simulation && !Lib.Time.IsInitialized)
			{
				return false;
			}
			TimeData val = (TimeData)(((??)SimulationTime) ?? Lib.Time.CurrentDateTime);
			int hour = ((TimeData)(ref val)).Hour;
			if (hour < OpeningHour || hour >= ClosingHour)
			{
				return false;
			}
			HashSet<WeekDay> hashSet = (from a in ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.DaysClosed.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
				select Enum.Parse<WeekDay>(a.ToLower())).ToHashSet();
			ref TimeData? simulationTime = ref SimulationTime;
			WeekDay val2;
			if (!simulationTime.HasValue)
			{
				val2 = Lib.Time.CurrentDayEnum;
			}
			else
			{
				TimeData valueOrDefault = simulationTime.GetValueOrDefault();
				val2 = ((TimeData)(ref valueOrDefault)).DayEnum;
			}
			WeekDay item = val2;
			if (hashSet.Contains(item))
			{
				return false;
			}
			return true;
		}

		private void OnClose()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			int historicalDataDeleted = 0;
			_stocks.ForEach(delegate(Stock a)
			{
				a.ClosingPrice = a.Price;
				a.CreateHistoricalData(null, SimulationTime);
				historicalDataDeleted += a.CleanUpHistoricalData(SimulationTime);
			});
			if (historicalDataDeleted > 0 && ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled && !_simulation)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(29, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Deleted ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(historicalDataDeleted);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" old historical data.");
				}
				log.LogInfo(val);
			}
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled && !_simulation)
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Stock market is closing.");
			}
		}

		private void OnOpen()
		{
			_stocks.ForEach(delegate(Stock a)
			{
				a.OpeningPrice = a.ClosingPrice.Value;
				a.ClosingPrice = null;
			});
			if (!_simulation)
			{
				TradeController.CreatePortfolioHistoricalDataEntry();
			}
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled && !_simulation)
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Stock market is opening.");
			}
		}

		private void Calculate()
		{
			foreach (Stock stock in _stocks)
			{
				stock.DeterminePrice();
			}
			this.OnCalculate?.Invoke(this, EventArgs.Empty);
		}

		private void GenerateTrends()
		{
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Expected O, but got Unknown
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Expected O, but got Unknown
			int num = 0;
			int maxHoursTrendsCanPersist = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.MaxHoursTrendsCanPersist;
			int minHoursTrendsMustPersist = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.MinHoursTrendsMustPersist;
			double stockTrendChancePercentage = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.StockTrendChancePercentage;
			int maxTrends = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.MaxTrends;
			bool isDebugEnabled = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled;
			if (maxTrends > -1 && Stocks.Count((Stock a) => a.Trend.HasValue) >= maxTrends)
			{
				return;
			}
			List<double> list = new List<double>();
			bool flag = default(bool);
			foreach (Stock item2 in Stocks.Where((Stock a) => !a.Trend.HasValue))
			{
				if (!(MathHelper.Random.NextDouble() * 100.0 < stockTrendChancePercentage))
				{
					continue;
				}
				double mean;
				double stdDev;
				if (item2.HistoricalData.Count >= 2)
				{
					for (int i = 1; i < item2.HistoricalData.Count; i++)
					{
						decimal value = item2.HistoricalData[i - 1].Close.Value;
						double item = (double)((item2.HistoricalData[i].Close.Value - value) / value * 100m);
						list.Add(item);
					}
					mean = list.Average();
					stdDev = MathHelper.CalculateStandardDeviation(list);
					list.Clear();
				}
				else
				{
					mean = 0.0;
					stdDev = 0.3;
				}
				double num2 = Math.Round(MathHelper.NextGaussian(mean, stdDev));
				int num3 = (int)num2;
				if (num3 == 0)
				{
					continue;
				}
				if (Math.Abs(num3) < 2)
				{
					num2 = ((num2 < 0.0) ? (num2 - 3.0) : (num2 + 3.0));
				}
				if (MathHelper.Random.NextDouble() * 100.0 < 7.0)
				{
					num2 *= (double)MathHelper.Random.Next(2, 5);
				}
				int steps = MathHelper.Random.Next(60 * minHoursTrendsMustPersist, 60 * maxHoursTrendsCanPersist);
				StockTrend stockTrend = new StockTrend(num2, item2.Price, steps);
				item2.SetTrend(stockTrend);
				num++;
				if (!_simulation)
				{
					NewsGenerator.GenerateArticle(item2, stockTrend);
				}
				if (isDebugEnabled && !_simulation)
				{
					ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(69, 6, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[NEW TREND]: \"(");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item2.Symbol);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item2.Name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" | Price: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(stockTrend.StartPrice);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | Target ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(stockTrend.EndPrice);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | Percentage: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(Math.Round(stockTrend.Percentage, 2));
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | MinutesLeft: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(stockTrend.Steps);
					}
					log.LogInfo(val);
				}
			}
			if (num > 0 && isDebugEnabled && !_simulation && Lib.Time.IsInitialized)
			{
				ManualLogSource log2 = PluginController<Plugin, IPluginBindings>.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[GameTime(");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<TimeData>(Lib.Time.CurrentDateTime);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")] Created ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" new trends.");
				}
				log2.LogInfo(val);
			}
		}

		private void OnFileSave(object sender, SaveGameArgs e)
		{
			string saveFilePath = GetSaveFilePath(e.FilePath);
			StockDataIO.Export(this, TradeController, saveFilePath);
		}

		private void OnFileLoad(object sender, SaveGameArgs e)
		{
			string saveFilePath = GetSaveFilePath(e.FilePath);
			if (!File.Exists(saveFilePath))
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Attempting to load a premod install savegame, a new market economy will be generated for this savegame.");
				InitPreModInstallEconomyExistingSavegame(saveFilePath, saveImmediately: true);
			}
			else if (!_isLoading)
			{
				_isLoading = true;
				_stocks.Clear();
				TradeController.Reset();
				NewsGenerator.Clear();
				_interiorCreatorFinished = true;
				_cityConstructorFinalized = true;
				_citizenCreatorFinished = true;
				Initialized = false;
				if (!StockDataIO.Import(this, TradeController, saveFilePath))
				{
					_isLoading = false;
					InitPreModInstallEconomyExistingSavegame(saveFilePath, saveImmediately: false);
				}
			}
		}

		private void OnFileDelete(object sender, SaveGameArgs e)
		{
			string saveFilePath = GetSaveFilePath(e.FilePath);
			if (File.Exists(saveFilePath))
			{
				File.Delete(saveFilePath);
			}
		}

		private void InitPreModInstallEconomyExistingSavegame(string filePath, bool saveImmediately)
		{
			_stocks.Clear();
			TradeController.Reset();
			NewsGenerator.Clear();
			CitizenCreatorPatches.CitizenCreator_Populate.Init = false;
			CityConstructorPatches.CityConstructor_Finalized.Init = false;
			CompanyPatches.Company_Setup.ShownInitializingMessage = false;
			InteriorCreatorPatches.InteriorCreator_GenChunk.Init = false;
			_interiorCreatorFinished = false;
			_cityConstructorFinalized = false;
			_citizenCreatorFinished = false;
			Initialized = false;
			PostStocksInitialization(typeof(CitizenCreator));
			PostStocksInitialization(typeof(InteriorCreator));
			PostStocksInitialization(typeof(CityConstructor));
			_afterPreModPath = filePath;
			if (!Lib.Time.IsInitialized)
			{
				Lib.Time.OnTimeInitialized += InitializeMarket;
				if (saveImmediately)
				{
					Lib.Time.OnTimeInitialized += AfterPreModInit;
				}
				else
				{
					_afterPreModPath = null;
				}
			}
			else
			{
				InitializeMarket(this, null);
				if (saveImmediately)
				{
					AfterPreModInit(this, null);
				}
				else
				{
					_afterPreModPath = null;
				}
			}
		}

		private void AfterPreModInit(object sender, TimeChangedArgs e)
		{
			Lib.Time.OnTimeInitialized -= AfterPreModInit;
			StockDataIO.Export(this, TradeController, _afterPreModPath);
			_afterPreModPath = null;
		}

		private static string GetSaveFilePath(string filePath)
		{
			string uniqueString = Lib.SaveGame.GetUniqueString(filePath);
			if (!Enum.TryParse<DataSaveFormat>(((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.StockDataSaveFormat.Trim(), ignoreCase: true, out var result))
			{
				throw new Exception("Invalid save format \"" + ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.StockDataSaveFormat + "\".");
			}
			string text = "." + result.ToString().ToLower();
			string text2 = "stocks_" + uniqueString + text;
			return Lib.SaveGame.GetSavestoreDirectoryPath(Assembly.GetExecutingAssembly(), text2);
		}
	}
	internal interface IStocksContainer
	{
		IReadOnlyList<Stock> Stocks { get; }
	}
	internal static class MathHelper
	{
		internal static MersenneTwister Random { get; private set; }

		internal static void Init(int seed)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			Random = new MersenneTwister(seed);
		}

		internal static void Init(MersenneTwister random)
		{
			Random = random;
		}

		private static double NextGaussian()
		{
			double num;
			double num3;
			do
			{
				num = 2.0 * Random.NextDouble() - 1.0;
				double num2 = 2.0 * Random.NextDouble() - 1.0;
				num3 = num * num + num2 * num2;
			}
			while (num3 >= 1.0 || num3 == 0.0);
			num3 = Math.Sqrt(-2.0 * Math.Log(num3) / num3);
			return num * num3;
		}

		public static double NextGaussian(double mean, double stdDev)
		{
			return mean + stdDev * NextGaussian();
		}

		public static double CalculateStandardDeviation(List<double> values)
		{
			double mean = values.Average();
			return Math.Sqrt(values.Sum((double value) => Math.Pow(value - mean, 2.0)) / (double)values.Count);
		}
	}
}
namespace SOD.StockMarket.Implementation.Trade
{
	internal static class Extensions
	{
		internal static string ToLimitedString(this decimal value, int max, CultureInfo culture = null)
		{
			return value.ToString(culture ?? CultureInfo.InvariantCulture).Substring(0, Math.Min(max, value.ToString(culture ?? CultureInfo.InvariantCulture).Length));
		}
	}
	internal class HistoricalPortfolio
	{
		[JsonIgnore]
		private TimeData? _timeData;

		[JsonIgnore]
		internal TimeData Date
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				TimeData valueOrDefault = _timeData.GetValueOrDefault();
				if (!_timeData.HasValue)
				{
					((TimeData)(ref valueOrDefault))..ctor(Year, Month, Day, 0, 0);
					_timeData = valueOrDefault;
					return valueOrDefault;
				}
				return valueOrDefault;
			}
			set
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				_timeData = value;
			}
		}

		public decimal Worth { get; set; }

		public int Year { get; set; }

		public int Month { get; set; }

		public int Day { get; set; }

		public HistoricalPortfolio()
		{
		}

		internal HistoricalPortfolio(TimeData date, decimal worth)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Date = date;
			Worth = worth;
			Year = ((TimeData)(ref date)).Year;
			Month = ((TimeData)(ref date)).Month;
			Day = ((TimeData)(ref date)).Day;
		}
	}
	internal class TradeController : IStocksContainer
	{
		internal readonly Market Market;

		private Dictionary<int, decimal> _playerStocks;

		private List<TradeOrder> _playerTradeOrders;

		private List<HistoricalPortfolio> _historicalPortfolio;

		private List<TradeHistory> _tradeHistory;

		internal bool NotificationsEnabled { get; set; } = true;


		internal decimal AvailableFunds { get; private set; }

		internal static int Money
		{
			get
			{
				return GameplayController.Instance.money;
			}
			set
			{
				GameplayController.Instance.money = value;
				FirstPersonItemController.Instance.PlayerMoneyCheck();
				if ((Object)(object)InterfaceControls.Instance.cashText != (Object)null)
				{
					((TMP_Text)InterfaceControls.Instance.cashText).text = CityControls.Instance.cityCurrency + Money;
					if ((Object)(object)BioScreenController.Instance.cashText != (Object)null)
					{
						((TMP_Text)BioScreenController.Instance.cashText).text = ((TMP_Text)InterfaceControls.Instance.cashText).text;
					}
				}
			}
		}

		public IReadOnlyList<Stock> Stocks => (from playerStock in _playerStocks
			join marketStock in Market.Stocks on playerStock.Key equals marketStock.Id
			select marketStock).ToList();

		public IReadOnlyList<TradeOrder> TradeOrders => _playerTradeOrders;

		public IReadOnlyList<HistoricalPortfolio> HistoricalPortfolioData => _historicalPortfolio;

		public IReadOnlyList<TradeHistory> TradeHistory => _tradeHistory;

		internal decimal TotalInvestedInStocks
		{
			get
			{
				decimal d = default(decimal);
				foreach (Stock stock in Stocks)
				{
					decimal num = _playerStocks[stock.Id];
					d += num * stock.Price;
				}
				Dictionary<int, Stock> stocks = Market.Stocks.ToDictionary((Stock a) => a.Id, (Stock a) => a);
				d += (from a in TradeOrders
					where a.OrderType == OrderType.Sell
					select stocks[a.StockId].Price * a.Amount).Sum();
				return Math.Round(d, 2);
			}
		}

		internal decimal PortfolioWorth
		{
			get
			{
				decimal num = (from a in TradeOrders
					where a.OrderType == OrderType.Buy
					select a.Price * a.Amount).Sum();
				return Math.Round(TotalInvestedInStocks + AvailableFunds + num, 2);
			}
		}

		internal decimal GetInvestedVolume(Stock stock)
		{
			if (!_playerStocks.TryGetValue(stock.Id, out var value))
			{
				return 0m;
			}
			return value;
		}

		internal TradeController(Market market)
		{
			Market = market;
			Market.OnCalculate += Market_OnCalculate;
			Import(null);
		}

		internal void CreatePortfolioHistoricalDataEntry()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			TimeData currentDate = Lib.Time.CurrentDate;
			if (!_historicalPortfolio.Any((HistoricalPortfolio a) => a.Date == currentDate))
			{
				_historicalPortfolio.Add(new HistoricalPortfolio(currentDate, PortfolioWorth));
			}
		}

		internal decimal? GetPortfolioPercentageChange(int days)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			TimeData currentDate = Lib.Time.CurrentDate;
			decimal? num = HistoricalPortfolioData.OrderByDescending((HistoricalPortfolio a) => a.Date).ToArray().FirstOrDefault((HistoricalPortfolio a) => (currentDate - a.Date).TotalDays >= (double)days)?.Worth;
			if (num.HasValue)
			{
				decimal? num2 = num;
				if (!((num2.GetValueOrDefault() == default(decimal)) & num2.HasValue))
				{
					decimal portfolioWorth = PortfolioWorth;
					decimal? num3 = num;
					return ((decimal?)portfolioWorth - num3) / num * (decimal?)100;
				}
			}
			return null;
		}

		internal void Reset()
		{
			_playerStocks.Clear();
			_playerTradeOrders.Clear();
			_historicalPortfolio.Clear();
			_tradeHistory.Clear();
			AvailableFunds = 0m;
		}

		internal void DepositFunds(int money)
		{
			if (money > 0 && Money >= money)
			{
				AvailableFunds += (decimal)money;
				Money -= money;
			}
		}

		internal void WithdrawFunds(int money)
		{
			if (money > 0 && AvailableFunds >= (decimal)money)
			{
				AvailableFunds -= (decimal)money;
				Money += money;
			}
		}

		internal void Import(TradeSaveData saveData)
		{
			_playerStocks = saveData?.PlayerStocks ?? new Dictionary<int, decimal>();
			_playerTradeOrders = saveData?.PlayerTradeOrders ?? new List<TradeOrder>();
			_historicalPortfolio = saveData?.HistoricalPortfolio ?? new List<HistoricalPortfolio>();
			_tradeHistory = saveData?.TradeHistory ?? new List<TradeHistory>();
			AvailableFunds = saveData?.AvailableFunds ?? 0m;
		}

		internal TradeSaveData Export()
		{
			return new TradeSaveData
			{
				PlayerStocks = _playerStocks.ToDictionary((KeyValuePair<int, decimal> a) => a.Key, (KeyValuePair<int, decimal> a) => a.Value),
				PlayerTradeOrders = _playerTradeOrders.ToList(),
				HistoricalPortfolio = _historicalPortfolio.ToList(),
				TradeHistory = _tradeHistory.ToList(),
				AvailableFunds = AvailableFunds
			};
		}

		internal bool InstantBuy(Stock stock, decimal amount, bool deductMoney = true)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (deductMoney && !IsValidOrder(OrderType.Buy, stock, amount))
			{
				return false;
			}
			if (deductMoney)
			{
				decimal num = Math.Round(stock.Price * amount, 2);
				AvailableFunds -= num;
			}
			if (_playerStocks.TryGetValue(stock.Id, out var value))
			{
				_playerStocks[stock.Id] = value + amount;
			}
			else
			{
				_playerStocks[stock.Id] = amount;
			}
			_tradeHistory.Add(new TradeHistory(Lib.Time.CurrentDateTime, stock.Symbol, OrderType.Buy, amount, stock.Price));
			return true;
		}

		internal bool InstantSell(Stock stock, decimal amount, bool removeStock = true)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (removeStock && !IsValidOrder(OrderType.Sell, stock, amount))
			{
				return false;
			}
			if (removeStock)
			{
				_playerStocks[stock.Id] -= amount;
				if (_playerStocks[stock.Id] <= 0m)
				{
					_playerStocks.Remove(stock.Id);
				}
			}
			decimal num = Math.Round(stock.Price * amount, 2);
			AvailableFunds += num;
			_tradeHistory.Add(new TradeHistory(Lib.Time.CurrentDateTime, stock.Symbol, OrderType.Sell, amount, stock.Price));
			return true;
		}

		internal bool BuyLimitOrder(Stock stock, decimal price, decimal amount)
		{
			if (!IsValidOrder(OrderType.Buy, stock, amount))
			{
				return false;
			}
			decimal num = Math.Round(stock.Price * amount, 2);
			AvailableFunds -= num;
			_playerTradeOrders.Add(new TradeOrder(OrderType.Buy, stock.Id, price, amount));
			return true;
		}

		internal bool SellLimitOrder(Stock stock, decimal price, decimal amount)
		{
			if (!IsValidOrder(OrderType.Sell, stock, amount))
			{
				return false;
			}
			_playerTradeOrders.Add(new TradeOrder(OrderType.Sell, stock.Id, price, amount));
			_playerStocks[stock.Id] -= amount;
			if (_playerStocks[stock.Id] <= 0m)
			{
				_playerStocks.Remove(stock.Id);
			}
			return true;
		}

		internal void CancelOrder(TradeOrder order)
		{
			if (order.Completed)
			{
				return;
			}
			if (order.OrderType == OrderType.Buy)
			{
				AvailableFunds += Math.Round(order.Price * order.Amount, 2);
			}
			else if (order.OrderType == OrderType.Sell)
			{
				if (_playerStocks.TryGetValue(order.StockId, out var value))
				{
					_playerStocks[order.StockId] = value + order.Amount;
				}
				else
				{
					_playerStocks[order.StockId] = order.Amount;
				}
			}
			order.Completed = true;
			_playerTradeOrders.Remove(order);
		}

		private void Market_OnCalculate(object sender, EventArgs e)
		{
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Expected O, but got Unknown
			foreach (TradeOrder order in _playerTradeOrders)
			{
				if (order.Completed)
				{
					continue;
				}
				Stock stock = Market.Stocks.FirstOrDefault((Stock a) => a.Id == order.StockId);
				if (stock == null)
				{
					order.Completed = true;
					continue;
				}
				decimal price = stock.Price;
				if ((order.OrderType == OrderType.Buy && price <= order.Price) || (order.OrderType == OrderType.Sell && price >= order.Price))
				{
					bool flag = false;
					flag = order.OrderType switch
					{
						OrderType.Buy => InstantBuy(stock, order.Amount, deductMoney: false), 
						OrderType.Sell => InstantSell(stock, order.Amount, removeStock: false), 
						_ => throw new NotImplementedException($"OrderType \"{order.OrderType}\" doesn't have an implementation."), 
					};
					order.Completed = flag;
					if (order.Completed && NotificationsEnabled)
					{
						Lib.GameMessage.Broadcast($"{order.OrderType} order \"({stock.Symbol}) '{order.Amount}' for target price € {order.Price}\" completed.", (GameMessageType)0, (Icon)17, (Color?)null, 0f);
					}
				}
			}
			_playerTradeOrders.RemoveAll((TradeOrder a) => a.Completed);
			TimeData currentDateTime = Lib.Time.CurrentDateTime;
			int num = 0;
			num += _tradeHistory.RemoveAll((TradeHistory a) => (currentDateTime - a.DateTime).TotalDays >= 7.0);
			num += _historicalPortfolio.RemoveAll((HistoricalPortfolio a) => (currentDateTime - a.Date).TotalDays >= 32.0);
			if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled && num > 0)
			{
				ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
				bool flag2 = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(31, 1, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Deleted ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" trade history entries.");
				}
				log.LogInfo(val);
			}
		}

		private bool IsValidOrder(OrderType orderType, Stock stock, decimal amount)
		{
			decimal value;
			return orderType switch
			{
				OrderType.Buy => AvailableFunds >= Math.Round(stock.Price * amount, 2), 
				OrderType.Sell => _playerStocks.TryGetValue(stock.Id, out value) && value >= amount, 
				_ => false, 
			};
		}
	}
	internal class TradeHistory
	{
		[JsonIgnore]
		private TimeData? _dateTime;

		[JsonIgnore]
		public TimeData DateTime
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				TimeData valueOrDefault = _dateTime.GetValueOrDefault();
				if (!_dateTime.HasValue)
				{
					((TimeData)(ref valueOrDefault))..ctor(Year, Month, Day, Hour, Minute);
					_dateTime = valueOrDefault;
					return valueOrDefault;
				}
				return valueOrDefault;
			}
			set
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				_dateTime = value;
			}
		}

		public int Year { get; set; }

		public int Month { get; set; }

		public int Day { get; set; }

		public int Hour { get; set; }

		public int Minute { get; set; }

		public string StockSymbol { get; set; }

		public OrderType OrderType { get; set; }

		public decimal Amount { get; set; }

		public decimal Price { get; set; }

		[JsonIgnore]
		public decimal Total => Math.Round(Price * Amount, 2);

		public TradeHistory()
		{
		}

		internal TradeHistory(TimeData dateTime, string symbol, OrderType orderType, decimal amount, decimal price)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			DateTime = dateTime;
			StockSymbol = symbol;
			OrderType = orderType;
			Amount = amount;
			Price = price;
			Year = ((TimeData)(ref dateTime)).Year;
			Month = ((TimeData)(ref dateTime)).Month;
			Day = ((TimeData)(ref dateTime)).Day;
			Hour = ((TimeData)(ref dateTime)).Hour;
			Minute = ((TimeData)(ref dateTime)).Minute;
		}
	}
	internal sealed class TradeHistoryPagination
	{
		private readonly int _maxTradeHistoryPerPage;

		private readonly TradeController _tradeController;

		private Func<TradeHistory, object> _currentSortingProperty;

		private bool _sortAscending = true;

		internal int CurrentPage { get; private set; }

		internal TradeHistory[] Current
		{
			get
			{
				TradeHistory[] array = new TradeHistory[_maxTradeHistoryPerPage];
				SetHistories(array);
				return array;
			}
		}

		internal TradeHistoryPagination(TradeController tradeController, int maxTradeHistoryPerPage)
		{
			_tradeController = tradeController;
			_maxTradeHistoryPerPage = maxTradeHistoryPerPage;
		}

		internal TradeHistory[] Next()
		{
			TradeHistory[] array = new TradeHistory[_maxTradeHistoryPerPage];
			int num = (int)Math.Ceiling((double)_tradeController.TradeHistory.Count / (double)_maxTradeHistoryPerPage);
			if (CurrentPage < num - 1)
			{
				CurrentPage++;
				SetHistories(array);
			}
			else if (CurrentPage == num - 1)
			{
				CurrentPage = 0;
				SetHistories(array);
			}
			return array;
		}

		internal TradeHistory[] Previous()
		{
			TradeHistory[] array = new TradeHistory[_maxTradeHistoryPerPage];
			if (CurrentPage > 0)
			{
				CurrentPage--;
				SetHistories(array);
			}
			else if (CurrentPage == 0)
			{
				int num = (int)Math.Ceiling((double)_tradeController.TradeHistory.Count / (double)_maxTradeHistoryPerPage);
				CurrentPage = Math.Max(0, num - 1);
				SetHistories(array);
			}
			return array;
		}

		internal TradeHistory[] Reset()
		{
			TradeHistory[] array = new TradeHistory[_maxTradeHistoryPerPage];
			CurrentPage = 0;
			SetHistories(array);
			return array;
		}

		internal void SortBy(Func<TradeHistory, object> selector, bool? ascending = null)
		{
			if (_currentSortingProperty == selector)
			{
				_sortAscending = !_sortAscending;
			}
			else
			{
				_sortAscending = true;
			}
			_currentSortingProperty = selector;
			if (ascending.HasValue)
			{
				_sortAscending = ascending.Value;
			}
		}

		private void SetHistories(TradeHistory[] slots)
		{
			if (_tradeController.TradeHistory.Count != 0)
			{
				IReadOnlyList<TradeHistory> readOnlyList = _tradeController.TradeHistory;
				if (_currentSortingProperty != null)
				{
					readOnlyList = (_sortAscending ? readOnlyList.OrderBy(_currentSortingProperty).ToList() : readOnlyList.OrderByDescending(_currentSortingProperty).ToList());
				}
				int num = CurrentPage * _maxTradeHistoryPerPage;
				for (int i = 0; i < _maxTradeHistoryPerPage; i++)
				{
					slots[i] = ((readOnlyList.Count > num + i) ? readOnlyList[num + i] : null);
				}
			}
		}
	}
	internal class TradeOrder
	{
		public int StockId { get; set; }

		public decimal Price { get; set; }

		public decimal Amount { get; set; }

		public OrderType OrderType { get; set; }

		public bool Completed { get; set; }

		public TradeOrder()
		{
		}

		internal TradeOrder(OrderType orderType, int stockId, decimal price, decimal amount)
		{
			StockId = stockId;
			Price = price;
			Amount = amount;
			OrderType = orderType;
			Completed = false;
		}
	}
	internal enum OrderType
	{
		Buy,
		Sell
	}
	internal sealed class TradeOrderPagination
	{
		private readonly int _maxTradeOrdersPerPage;

		private readonly TradeController _tradeController;

		private Stock _forStockOnly;

		internal int CurrentPage { get; private set; }

		internal StockOrder[] Current
		{
			get
			{
				StockOrder[] array = new StockOrder[_maxTradeOrdersPerPage];
				SetStocks(array);
				return array;
			}
		}

		internal TradeOrderPagination(TradeController tradeController, int maxTradeOrdersPerPage)
		{
			_tradeController = tradeController;
			_maxTradeOrdersPerPage = maxTradeOrdersPerPage;
		}

		internal void SetForStockOnly(Stock stock)
		{
			_forStockOnly = stock;
		}

		internal StockOrder[] Next()
		{
			StockOrder[] array = new StockOrder[_maxTradeOrdersPerPage];
			int num = (int)Math.Ceiling((double)_tradeController.TradeOrders.Count / (double)_maxTradeOrdersPerPage);
			if (CurrentPage < num - 1)
			{
				CurrentPage++;
				SetStocks(array);
			}
			else if (CurrentPage == num - 1)
			{
				CurrentPage = 0;
				SetStocks(array);
			}
			return array;
		}

		internal StockOrder[] Previous()
		{
			StockOrder[] array = new StockOrder[_maxTradeOrdersPerPage];
			if (CurrentPage > 0)
			{
				CurrentPage--;
				SetStocks(array);
			}
			else if (CurrentPage == 0)
			{
				int num = (int)Math.Ceiling((double)_tradeController.TradeOrders.Count / (double)_maxTradeOrdersPerPage);
				CurrentPage = num - 1;
				SetStocks(array);
			}
			return array;
		}

		internal StockOrder[] Reset()
		{
			StockOrder[] array = new StockOrder[_maxTradeOrdersPerPage];
			CurrentPage = 0;
			SetStocks(array);
			return array;
		}

		private void SetStocks(StockOrder[] stocks)
		{
			IReadOnlyList<TradeOrder> readOnlyList = _tradeController.TradeOrders;
			if (_forStockOnly != null)
			{
				readOnlyList = readOnlyList.Where((TradeOrder a) => a.StockId == _forStockOnly.Id).ToList();
			}
			if (readOnlyList.Count == 0)
			{
				return;
			}
			int num = CurrentPage * _maxTradeOrdersPerPage;
			for (int i = 0; i < _maxTradeOrdersPerPage; i++)
			{
				TradeOrder order = ((readOnlyList.Count > num + i) ? readOnlyList[num + i] : null);
				if (order == null)
				{
					stocks[i] = null;
					continue;
				}
				Stock stock = _tradeController.Market.Stocks.FirstOrDefault((Stock a) => a.Id == order.StockId);
				if (stock == null)
				{
					stocks[i] = null;
				}
				else
				{
					stocks[i] = new StockOrder(stock, order);
				}
			}
		}
	}
	internal sealed class StockOrder
	{
		internal Stock Stock { get; }

		internal TradeOrder TradeOrder { get; }

		internal StockOrder(Stock stock, TradeOrder tradeOrder)
		{
			Stock = stock;
			TradeOrder = tradeOrder;
		}
	}
	internal sealed class TradeSaveData
	{
		public Dictionary<int, decimal> PlayerStocks { get; set; }

		public List<TradeOrder> PlayerTradeOrders { get; set; }

		public List<HistoricalPortfolio> HistoricalPortfolio { get; set; }

		public List<TradeHistory> TradeHistory { get; set; }

		public decimal AvailableFunds { get; set; }

		internal string ToJson()
		{
			return JsonSerializer.Serialize(this, new JsonSerializerOptions
			{
				WriteIndented = false
			});
		}

		internal static TradeSaveData FromJson(string json)
		{
			return JsonSerializer.Deserialize<TradeSaveData>(json);
		}
	}
}
namespace SOD.StockMarket.Implementation.Stocks
{
	internal class CompanyStockData
	{
		private Company _company;

		private decimal? _averageSales;

		private decimal? _minSalary;

		private decimal? _topSalary;

		internal string Name { get; private set; }

		internal string Symbol { get; private set; }

		internal decimal AverageSales => GetAverageSales();

		internal decimal MinSalary => GetMinSalary();

		internal decimal TopSalary => GetTopSalary();

		internal double Volatility { get; private set; }

		internal CompanyStockData(string name, double volatility, string symbol = null)
		{
			Name = name;
			Symbol = symbol ?? StockSymbolGenerator.Generate(Name);
			Volatility = volatility;
		}

		internal CompanyStockData(Company company)
		{
			_company = company;
		}

		internal void Initialize()
		{
			if (_company != null)
			{
				Name = _company.name;
				Symbol = StockSymbolGenerator.Generate(Name);
				Volatility = Math.Round(MathHelper.Random.NextDouble(0.15, 0.85), 2);
			}
			_company = null;
		}

		private decimal GetAverageSales()
		{
			decimal valueOrDefault = _averageSales.GetValueOrDefault();
			if (!_averageSales.HasValue)
			{
				valueOrDefault = MathHelper.Random.Next(5, 1000);
				_averageSales = valueOrDefault;
				return valueOrDefault;
			}
			return valueOrDefault;
		}

		private decimal GetMinSalary()
		{
			if (_company != null && _company.minimumSalary > 0f)
			{
				return (decimal)_company.minimumSalary;
			}
			decimal valueOrDefault = _minSalary.GetValueOrDefault();
			if (!_minSalary.HasValue)
			{
				valueOrDefault = MathHelper.Random.Next(550, 1350);
				_minSalary = valueOrDefault;
				return valueOrDefault;
			}
			return valueOrDefault;
		}

		private decimal GetTopSalary()
		{
			if (_company != null && _company.topSalary > 0f)
			{
				return (decimal)_company.topSalary;
			}
			decimal valueOrDefault = _topSalary.GetValueOrDefault();
			if (!_topSalary.HasValue)
			{
				valueOrDefault = MathHelper.Random.Next((int)GetMinSalary() + 1, 2250);
				_topSalary = valueOrDefault;
				return valueOrDefault;
			}
			return valueOrDefault;
		}
	}
	internal static class CustomStocks
	{
		internal static readonly (CompanyStockData data, decimal? basePrice)[] Stocks = new(CompanyStockData, decimal?)[3]
		{
			(new CompanyStockData("Starch Kola", 0.5), (decimal)MathHelper.Random.NextDouble(5000.0, 10000.0)),
			(new CompanyStockData("Kaizen-7", 0.4), (decimal)MathHelper.Random.NextDouble(3500.0, 7500.0)),
			(new CompanyStockData("Crow Coin", 0.15), (decimal)MathHelper.Random.NextDouble(0.9750000238418579, 1.024999976158142))
		};
	}
	internal class Stock
	{
		private static int _id;

		private readonly List<StockData> _historicalData;

		private readonly CompanyStockData _companyData;

		private readonly decimal? _basePrice;

		private readonly bool _imported;

		private int _currentStep;

		internal int Id { get; }

		internal string Name => _companyData.Name;

		internal string Symbol => _companyData.Symbol;

		internal double Volatility => _companyData.Volatility;

		internal decimal Price { get; private set; }

		internal decimal OpeningPrice { get; set; }

		internal decimal? ClosingPrice { get; set; }

		internal decimal HighPrice { get; private set; }

		internal decimal LowPrice { get; private set; }

		internal StockTrend? Trend { get; private set; }

		[JsonIgnore]
		internal decimal TodayDiff => Math.Round(Price - OpeningPrice, 2);

		[JsonIgnore]
		internal decimal DailyPercentage => GetPercentage(Price, OpeningPrice);

		[JsonIgnore]
		internal decimal? WeeklyPercentage
		{
			get
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				TimeData currentDate = Lib.Time.CurrentDate;
				StockData stockData = HistoricalData.OrderByDescending((StockData a) => a.Date).FirstOrDefault((StockData a) => (currentDate - a.Date).TotalDays >= 7.0);
				if (stockData == null)
				{
					return null;
				}
				return GetPercentage(Price, stockData.Open);
			}
		}

		[JsonIgnore]
		internal decimal? MonthlyPercentage
		{
			get
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				TimeData currentDate = Lib.Time.CurrentDate;
				StockData stockData = HistoricalData.OrderByDescending((StockData a) => a.Date).FirstOrDefault((StockData a) => (currentDate - a.Date).TotalDays >= 30.0);
				if (stockData == null)
				{
					return null;
				}
				return GetPercentage(Price, stockData.Open);
			}
		}

		internal IReadOnlyList<StockData> HistoricalData => _historicalData;

		internal Stock(Company company)
			: this()
		{
			_companyData = new CompanyStockData(company);
		}

		internal Stock(CompanyStockData companyData, decimal? basePrice = null)
			: this((int?)null, basePrice)
		{
			_companyData = companyData;
		}

		internal Stock(StockDataIO.StockDataDTO dto, IEnumerable<StockData> historicalData)
			: this(dto.Id, dto.Price)
		{
			_companyData = new CompanyStockData(dto.Name, dto.Volatility.Value, dto.Symbol);
			Price = dto.Price.Value;
			OpeningPrice = dto.Open;
			ClosingPrice = dto.Close;
			LowPrice = dto.Low;
			HighPrice = dto.High;
			if (dto.TrendPercentage.HasValue && dto.TrendStartPrice.HasValue && dto.TrendEndPrice.HasValue && dto.TrendSteps.HasValue)
			{
				StockTrend value = new StockTrend
				{
					Percentage = dto.TrendPercentage.Value,
					StartPrice = dto.TrendStartPrice.Value,
					EndPrice = dto.TrendEndPrice.Value,
					Steps = dto.TrendSteps.Value
				};
				Trend = value;
			}
			foreach (StockData item in historicalData.OrderBy((StockData a) => a.Date))
			{
				CreateHistoricalData(item);
			}
			_imported = true;
		}

		internal Stock(Stock stock)
		{
			Id = stock.Id;
			_companyData = stock._companyData;
			Price = stock.Price;
			OpeningPrice = stock.OpeningPrice;
			ClosingPrice = stock.ClosingPrice;
			LowPrice = stock.LowPrice;
			HighPrice = stock.HighPrice;
			Trend = stock.Trend;
			_historicalData = new List<StockData>();
			foreach (StockData historicalDatum in stock._historicalData)
			{
				_historicalData.Add(new StockData(historicalDatum));
			}
			_basePrice = stock._basePrice;
			_imported = stock._imported;
		}

		private Stock(int? id = null, decimal? basePrice = null)
		{
			Id = id ?? _id++;
			_historicalData = new List<StockData>();
			_basePrice = basePrice;
			_imported = false;
		}

		private static decimal GetPercentage(decimal currentPrice, decimal openingPrice)
		{
			double num = ((openingPrice != 0m) ? ((double)((currentPrice - openingPrice) / openingPrice * 100m)) : ((currentPrice > 0m) ? double.PositiveInfinity : ((!(currentPrice < 0m)) ? 0.0 : double.NegativeInfinity)));
			return Math.Round((decimal)num, 2);
		}

		internal void DeterminePrice()
		{
			if ((!Trend.HasValue || _currentStep >= Trend.Value.Steps) && MathHelper.Random.Next(0, 99) < 10)
			{
				if (Trend.HasValue && _currentStep >= Trend.Value.Steps)
				{
					RemoveTrend();
				}
				return;
			}
			decimal? num = null;
			if (Trend.HasValue)
			{
				StockTrend value = Trend.Value;
				if (_currentStep >= value.Steps)
				{
					RemoveTrend();
				}
				else
				{
					decimal num2 = (decimal)_currentStep / (decimal)value.Steps;
					num = value.StartPrice + (value.EndPrice - value.StartPrice) * num2;
					_currentStep++;
					if (_currentStep >= value.Steps)
					{
						RemoveTrend();
					}
				}
			}
			if (!num.HasValue)
			{
				decimal num3 = (decimal)((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.PriceFluctuationPercentage * (decimal)Volatility;
				decimal num4 = Price * (num3 / 100m);
				num = Price + (decimal)(MathHelper.Random.NextDouble() * (double)num4 * 2.0) - num4;
			}
			if (num.HasValue)
			{
				decimal? num5 = num;
				if (!((num5.GetValueOrDefault() <= default(decimal)) & num5.HasValue))
				{
					goto IL_0222;
				}
			}
			num = 0.01m;
			if (Trend.HasValue && Trend.Value.EndPrice <= 0.01m)
			{
				RemoveTrend();
			}
			goto IL_0222;
			IL_0222:
			Price = Math.Round(num.Value, 2);
			UpdateHighestLowestPrices();
		}

		internal void SetTrend(StockTrend stockTrend)
		{
			if (!Trend.HasValue)
			{
				Trend = stockTrend;
			}
		}

		internal void RemoveTrend()
		{
			_currentStep = 0;
			Trend = null;
		}

		internal void CreateHistoricalData(StockData stockData = null, TimeData? date = null)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (stockData != null)
			{
				_historicalData.Add(stockData);
				return;
			}
			TimeData date2 = (TimeData)(((??)date) ?? Lib.Time.CurrentDate);
			_historicalData.Add(new StockData
			{
				Date = date2,
				Close = ClosingPrice,
				Open = OpeningPrice,
				Low = LowPrice,
				High = HighPrice,
				Trend = Trend
			});
			LowPrice = ClosingPrice.Value;
			HighPrice = ClosingPrice.Value;
		}

		internal int CleanUpHistoricalData(TimeData? date)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			TimeData currentDate = (TimeData)(((??)date) ?? Lib.Time.CurrentDate);
			int maxDays = ((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.DaysToKeepStockHistoricalData;
			return _historicalData.RemoveAll((StockData stockData) => ((currentDate - stockData.Date).Days > maxDays) ? true : false);
		}

		internal void Initialize()
		{
			if (_imported)
			{
				throw new Exception("An imported stock is already initialized, no need to call Initialize();");
			}
			_companyData.Initialize();
			Price = Math.Round(_basePrice ?? (_companyData.AverageSales / (_companyData.MinSalary + _companyData.TopSalary) * 1000m), 2);
			OpeningPrice = Price;
			LowPrice = Price;
			HighPrice = Price;
		}

		private void UpdateHighestLowestPrices()
		{
			if (LowPrice > Price)
			{
				LowPrice = Price;
			}
			else if (HighPrice < Price)
			{
				HighPrice = Price;
			}
		}
	}
	internal class StockData : IEquatable<StockData>
	{
		public TimeData Date { get; set; }

		public decimal Open { get; set; }

		public decimal? Close { get; set; }

		public decimal High { get; set; }

		public decimal Low { get; set; }

		public StockTrend? Trend { get; set; }

		internal StockData()
		{
		}

		internal StockData(StockData data)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Date = data.Date;
			Open = data.Open;
			Close = data.Close;
			High = data.High;
			Low = data.Low;
			Trend = data.Trend;
		}

		public bool Equals(StockData other)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (other != null)
			{
				TimeData date = other.Date;
				return ((TimeData)(ref date)).Equals(Date);
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			return Equals(obj as StockData);
		}

		public override int GetHashCode()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			TimeData date = Date;
			return ((object)(TimeData)(ref date)).GetHashCode();
		}
	}
	internal static class StockDataIO
	{
		internal sealed class StockDataDTO
		{
			public int Id { get; set; }

			public string Name { get; set; }

			public string Symbol { get; set; }

			public TimeData? Date { get; set; }

			public decimal Open { get; set; }

			public decimal? Close { get; set; }

			public decimal High { get; set; }

			public decimal Low { get; set; }

			public decimal Average { get; set; }

			public decimal? Price { get; set; }

			public double? Volatility { get; set; }

			public double? TrendPercentage { get; set; }

			public decimal? TrendStartPrice { get; set; }

			public decimal? TrendEndPrice { get; set; }

			public int? TrendSteps { get; set; }

			public TradeSaveData TradeSaveData { get; set; }

			public List<Article> Articles { get; set; }

			public decimal? OriginalPrice { get; set; }

			public decimal SimulationChange => Math.Round(Price.Value - OriginalPrice.Value, 2);
		}

		internal static void Export(Market market, TradeController tradeController, string path, Market simulation = null)
		{
			//IL_0536: Unknown result type (might be due to invalid IL or missing references)
			//IL_053d: Expected O, but got Unknown
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			if (!market.Initialized)
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Cannot export stock market data, market not yet initialized.");
				return;
			}
			List<StockDataDTO> list = new List<StockDataDTO>
			{
				new StockDataDTO
				{
					TradeSaveData = tradeController.Export()
				},
				new StockDataDTO
				{
					Articles = NewsGenerator.AllArticles.ToList()
				}
			};
			foreach (Stock stock in market.Stocks.OrderBy((Stock a) => a.Id))
			{
				StockDataDTO stockDataDTO = new StockDataDTO
				{
					Id = stock.Id,
					Name = stock.Name,
					Symbol = stock.Symbol,
					Date = null,
					Price = stock.Price,
					Open = stock.OpeningPrice,
					Close = stock.ClosingPrice,
					High = stock.HighPrice,
					Low = stock.LowPrice,
					Volatility = stock.Volatility,
					TrendPercentage = stock.Trend?.Percentage,
					TrendStartPrice = stock.Trend?.StartPrice,
					TrendEndPrice = stock.Trend?.EndPrice,
					TrendSteps = stock.Trend?.Steps,
					Average = Math.Round((stock.HighPrice + stock.LowPrice + stock.Price) / 3m, 2)
				};
				if (simulation != null)
				{
					Stock stock2 = simulation.Stocks.First((Stock a) => a.Id == stock.Id);
					stockDataDTO.OriginalPrice = stock2.Price;
				}
				list.Add(stockDataDTO);
				foreach (StockData item2 in stock.HistoricalData.OrderBy((StockData a) => a.Date))
				{
					StockDataDTO item = new StockDataDTO
					{
						Id = stock.Id,
						Name = stock.Name,
						Symbol = stock.Symbol,
						Date = item2.Date,
						Open = item2.Open,
						Close = item2.Close,
						High = item2.High,
						Low = item2.Low,
						Average = Math.Round((item2.Open + item2.Close.Value + item2.High + item2.Low) / 4m),
						Price = null,
						Volatility = null,
						TrendPercentage = item2.Trend?.Percentage,
						TrendStartPrice = item2.Trend?.StartPrice,
						TrendEndPrice = item2.Trend?.EndPrice,
						TrendSteps = item2.Trend?.Steps,
						OriginalPrice = null
					};
					list.Add(item);
				}
			}
			ConverterFactory.Get(path).Save(list, MathHelper.Random, path, simulation != null);
			ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Exported ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(list.Count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" stock market data rows.");
			}
			log.LogInfo(val);
		}

		internal static bool Import(Market market, TradeController tradeController, string path)
		{
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Expected O, but got Unknown
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Expected O, but got Unknown
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Expected O, but got Unknown
			if (market.Initialized)
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Cannot import stock market data, market is already initialized.");
				return true;
			}
			bool flag = default(bool);
			try
			{
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Loading stock data..");
				List<StockDataDTO> list = ConverterFactory.Get(path).Load(path);
				Dictionary<int, StockData[]> dictionary = (from a in list
					where a.TradeSaveData == null && a.Articles == null && !a.Price.HasValue
					group a by a.Id).Select(delegate(IGrouping<int, StockDataDTO> a)
				{
					IEnumerable<StockData> source = a.OrderBy((StockDataDTO a) => a.Date).Select(delegate(StockDataDTO a)
					{
						//IL_0033: Unknown result type (might be due to invalid IL or missing references)
						StockData stockData = new StockData
						{
							Close = a.Close,
							High = a.High,
							Low = a.Low,
							Date = a.Date.Value,
							Open = a.Open
						};
						if (a.TrendPercentage.HasValue && a.TrendStartPrice.HasValue && a.TrendEndPrice.HasValue && a.TrendSteps.HasValue)
						{
							StockTrend stockTrend = default(StockTrend);
							stockTrend.Percentage = a.TrendPercentage.Value;
							stockTrend.StartPrice = a.TrendStartPrice.Value;
							stockTrend.EndPrice = a.TrendEndPrice.Value;
							stockTrend.Steps = a.TrendSteps.Value;
							StockTrend value = stockTrend;
							stockData.Trend = value;
						}
						return stockData;
					});
					return new
					{
						Key = a.Key,
						Data = source.ToArray()
					};
				}).ToDictionary(a => a.Key, a => a.Data);
				foreach (StockDataDTO item in from a in list
					where a.TradeSaveData == null && a.Articles == null && a.Price.HasValue
					orderby a.Id
					select a)
				{
					Stock stock = new Stock(item, dictionary[item.Id]);
					market.InitStock(stock);
				}
				TradeSaveData tradeSaveData = list[0].TradeSaveData;
				if (tradeSaveData != null)
				{
					PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Loading trade data..");
					tradeController.Import(tradeSaveData);
				}
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Loading news data..");
				NewsGenerator.Import(list[1].Articles);
				if (((PluginController<Plugin, IPluginBindings>)PluginController<Plugin, IPluginBindings>.Instance).Config.IsDebugEnabled)
				{
					PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)("Stocks data loaded: " + market.Stocks.Count));
					ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(17, 0, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("- Loaded stocks -");
					}
					log.LogInfo(val);
					foreach (Stock item2 in market.Stocks.OrderBy((Stock a) => a.Id))
					{
						ManualLogSource log2 = PluginController<Plugin, IPluginBindings>.Log;
						val = new BepInExInfoLogInterpolatedStringHandler(23, 3, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Stock: \"(");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item2.Symbol);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item2.Name);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" | Price: ");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<decimal>(item2.Price);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
						}
						log2.LogInfo(val);
					}
					ManualLogSource log3 = PluginController<Plugin, IPluginBindings>.Log;
					val = new BepInExInfoLogInterpolatedStringHandler(17, 0, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("- End of Stocks -");
					}
					log3.LogInfo(val);
				}
				PluginController<Plugin, IPluginBindings>.Log.LogInfo((object)"Stock market loaded.");
			}
			catch (Exception)
			{
				ManualLogSource log4 = PluginController<Plugin, IPluginBindings>.Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(86, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("The savefile is not compatible with \"Stockmarket v");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("2.0.6");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\", skipped loading stockmarket data.");
				}
				log4.LogInfo(val);
				return false;
			}
			market.PostStocksInitialization(typeof(StockDataIO));
			return true;
		}
	}
	internal static class StockNameGenerator
	{
		private static readonly Faker faker = new Faker("en");

		internal static string GenerateStockName()
		{
			return faker.Company.CompanyName((int?)null);
		}
	}
	internal sealed class StockPagination
	{
		private readonly IStocksContainer _stockContainer;

		private readonly Func<int> _maxStocksPerPageFunc;

		private Func<Stock, object> _currentSortingProperty;

		private bool _sortAscending = true;

		internal int CurrentPage { get; private set; }

		internal Stock[] Current
		{
			get
			{
				Stock[] array = new Stock[_maxStocksPerPageFunc()];
				SetStocks(array);
				return array;
			}
		}

		internal StockPagination(IStocksContainer stockContainer, int maxStocksPerPage)
		{
			_stockContainer = stockContainer;
			_maxStocksPerPageFunc = () => maxStocksPerPage;
		}

		internal StockPagination(IStocksContainer stockContainer, Func<int> maxStocksPerPage)
		{
			_stockContainer = stockContainer;
			_maxStocksPerPageFunc = maxStocksPerPage;
		}

		internal Stock[] Next()
		{
			int num = _maxStocksPerPageFunc();
			Stock[] array = new Stock[num];
			int num2 = (int)Math.Ceiling((double)_stockContainer.Stocks.Count / (double)num);
			if (CurrentPage < num2 - 1)
			{
				CurrentPage++;
				SetStocks(array);
			}
			else if (CurrentPage == num2 - 1)
			{
				CurrentPage = 0;
				SetStocks(array);
			}
			return array;
		}

		internal Stock[] Previous()
		{
			int num = _maxStocksPerPageFunc();
			Stock[] array = new Stock[num];
			if (CurrentPage > 0)
			{
				CurrentPage--;
				SetStocks(array);
			}
			else if (CurrentPage == 0)
			{
				int num2 = (int)Math.Ceiling((double)_stockContainer.Stocks.Count / (double)num);
				CurrentPage = num2 - 1;
				SetStocks(array);
			}
			return array;
		}

		internal Stock[] Reset()
		{
			Stock[] array = new Stock[_maxStocksPerPageFunc()];
			CurrentPage = 0;
			SetStocks(array);
			return array;
		}

		internal void SortBy(Func<Stock, object> selector, bool? ascending = null)
		{
			if (_currentSortingProperty == selector)
			{
				_sortAscending = !_sortAscending;
			}
			else
			{
				_sortAscending = true;
			}
			_currentSortingProperty = selector;
			if (ascending.HasValue)
			{
				_sortAscending = ascending.Value;
			}
		}

		private void SetStocks(Stock[] stocks)
		{
			if (_stockContainer.Stocks.Count != 0)
			{
				IReadOnlyList<Stock> readOnlyList = _stockContainer.Stocks;
				if (_currentSortingProperty != null)
				{
					readOnlyList = (_sortAscending ? readOnlyList.OrderBy(_currentSortingProperty).ToList() : readOnlyList.OrderByDescending(_currentSortingProperty).ToList());
				}
				int num = _maxStocksPerPageFunc();
				int num2 = CurrentPage * num;
				for (int i = 0; i < num; i++)
				{
					Stock stock = ((readOnlyList.Count > num2 + i) ? readOnlyList[num2 + i] : null);
					stocks[i] = stock;
				}
			}
		}
	}
	internal static class StockSymbolGenerator
	{
		private static Dictionary<string, int> _companySymbolCount;

		internal static string Generate(string companyName)
		{
			string text = GetSymbol(companyName);
			if (_companySymbolCount == null)
			{
				_companySymbolCount = new Dictionary<string, int>();
			}
			if (_companySymbolCount.TryGetValue(text, out var value))
			{
				value++;
				_companySymbolCount[text] = value;
			}
			else
			{
				_companySymbolCount[text] = 0;
			}
			if (text.Length == 4 && value > 0)
			{
				text = new string(text.Take(3).ToArray());
				if (_companySymbolCount.TryGetValue(text, out value))
				{
					value++;
					_companySymbolCount[text] = value;
				}
				else
				{
					_companySymbolCount[text] = 0;
				}
			}
			string text2 = text + value;
			if (text2.Length == 1)
			{
				text2 = text + value.ToString("D3");
			}
			else if (text2.Length == 2)
			{
				text2 = text + value.ToString("D2");
			}
			text2 = text2.Substring(0, Math.Min(text2.Length, 4));
			return text2.ToUpper();
		}

		internal static void Clear()
		{
			_companySymbolCount = null;
		}

		private static string GetSymbol(string companyName)
		{
			char[] blockedChars = new char[3] { '-', '_', '&' };
			string[] array = (from a in companyName.Split(' ', StringSplitOptions.RemoveEmptyEntries)
				where a.Length > 1
				select a).ToArray();
			if (array.Length == 0)
			{
				throw new Exception("Missing company name.");
			}
			if (array.Length == 1)
			{
				return new string(array[0].Where((char a) => !blockedChars.Contains(a)).Take(4).ToArray());
			}
			return new string(array[0].Where((char a) => !blockedChars.Contains(a)).Take(2).Concat(array[1].Where((char a) => !blockedChars.Contains(a)).Take(2))
				.ToArray());
		}
	}
	internal readonly struct StockTrend
	{
		internal double Percentage { get; init; }

		internal decimal StartPrice { get; init; }

		internal decimal EndPrice { get; init; }

		internal int Steps { get; init; }

		internal StockTrend(double percentage, decimal currentPrice, int steps)
		{
			Percentage = percentage;
			StartPrice = currentPrice;
			Steps = steps;
			EndPrice = Math.Round(StartPrice + StartPrice / 100m * (decimal)Percentage, 2);
		}
	}
}
namespace SOD.StockMarket.Implementation.DataConversion
{
	internal static class ConverterFactory
	{
		private static readonly Dictionary<string, Func<IDataConverter>> _converters = new Dictionary<string, Func<IDataConverter>>(StringComparer.OrdinalIgnoreCase)
		{
			{
				".csv",
				() => CsvConverter.Create()
			},
			{
				".bin",
				() => BinaryConverter.Create()
			}
		};

		public static IDataConverter Get(string filePath)
		{
			string extension = Path.GetExtension(filePath);
			if (_converters.TryGetValue(extension, out var value))
			{
				return value();
			}
			throw new NotSupportedException("Data type \"" + extension + "\" is not supported.");
		}
	}
	public enum DataSaveFormat
	{
		Csv,
		Bin
	}
	internal interface IDataConverter
	{
		void Save(List<StockDataIO.StockDataDTO> data, MersenneTwister random, string path, bool simulation = false);

		List<StockDataIO.StockDataDTO> Load(string path);
	}
}
namespace SOD.StockMarket.Implementation.DataConversion.Converters
{
	internal sealed class BinaryConverter : IDataConverter
	{
		private BinaryConverter()
		{
		}

		internal static BinaryConverter Create()
		{
			return new BinaryConverter();
		}

		public void Save(List<StockDataIO.StockDataDTO> data, MersenneTwister random, string path, bool simulation = false)
		{
			using BinaryWriter binaryWriter = new BinaryWriter(new FileStream(path, FileMode.Create, FileAccess.Write));
			binaryWriter.Write("BinaryStockData");
			var (value, array) = random.SaveState();
			binaryWriter.Write(value);
			uint[] array2 = array;
			foreach (uint value2 in array2)
			{
				binaryWriter.Write(value2);
			}
			string value3 = data[0].TradeSaveData.ToJson();
			binaryWriter.Write(value3);
			string value4 = JsonSerializer.Serialize(data[1].Articles, new JsonSerializerOptions
			{
				WriteIndented = false
			});
			binaryWriter.Write(value4);
			for (int j = 2; j < data.Count; j++)
			{
				StockDataIO.StockDataDTO stockDataDTO = data[j];
				binaryWriter.Write(stockDataDTO.Id);
				binaryWriter.Write(stockDataDTO.Name);
				binaryWriter.Write(stockDataDTO.Symbol);
				WriteNullableTimeData(binaryWriter, stockDataDTO.Date);
				WriteNullableDecimal(binaryWriter, stockDataDTO.Price);
				binaryWriter.Write(stockDataDTO.Open);
				WriteNullableDecimal(binaryWriter, stockDataDTO.Close);
				binaryWriter.Write(stockDataDTO.High);
				binaryWriter.Write(stockDataDTO.Low);
				WriteNullableDouble(binaryWriter, stockDataDTO.Volatility);
				WriteNullableDouble(binaryWriter, stockDataDTO.TrendPercentage);
				WriteNullableDecimal(binaryWriter, stockDataDTO.TrendStartPrice);
				WriteNullableDecimal(binaryWriter, stockDataDTO.TrendEndPrice);
				WriteNullableInt(binaryWriter, stockDataDTO.TrendSteps);
				binaryWriter.Write(stockDataDTO.Average);
			}
		}

		public List<StockDataIO.StockDataDTO> Load(string path)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			List<StockDataIO.StockDataDTO> list = new List<StockDataIO.StockDataDTO>();
			using BinaryReader binaryReader = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read));
			if (binaryReader.ReadString() != "BinaryStockData")
			{
				throw new InvalidDataException("Invalid binary file format.");
			}
			int item = binaryReader.ReadInt32();
			uint[] array = new uint[624];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = binaryReader.ReadUInt32();
			}
			MathHelper.Init(new MersenneTwister((item, array)));
			TradeSaveData tradeSaveData = TradeSaveData.FromJson(binaryReader.ReadString());
			list.Add(new StockDataIO.StockDataDTO
			{
				TradeSaveData = tradeSaveData
			});
			List<Article> articles = JsonSerializer.Deserialize<List<Article>>(binaryReader.ReadString());
			list.Add(new StockDataIO.StockDataDTO
			{
				Articles = articles
			});
			while (binaryReader.BaseStream.Position < binaryReader.BaseStream.Length)
			{
				StockDataIO.StockDataDTO item2 = new StockDataIO.StockDataDTO
				{
					Id = binaryReader.ReadInt32(),
					Name = binaryReader.ReadString(),
					Symbol = binaryReader.ReadString(),
					Date = ReadNullableTimeData(binaryReader),
					Price = ReadNullableDecimal(binaryReader),
					Open = binaryReader.ReadDecimal(),
					Close = ReadNullableDecimal(binaryReader),
					High = binaryReader.ReadDecimal(),
					Low = binaryReader.ReadDecimal(),
					Volatility = ReadNullableDouble(binaryReader),
					TrendPercentage = ReadNullableDouble(binaryReader),
					TrendStartPrice = ReadNullableDecimal(binaryReader),
					TrendEndPrice = ReadNullableDecimal(binaryReader),
					TrendSteps = ReadNullableInt(binaryReader),
					Average = binaryReader.ReadDecimal()
				};
				list.Add(item2);
			}
			return list;
		}

		private static void WriteNullableTimeData(BinaryWriter writer, TimeData? value)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			writer.Write(value.HasValue);
			if (value.HasValue)
			{
				TimeData value2 = value.Value;
				writer.Write(((TimeData)(ref value2)).Serialize());
			}
		}

		private static void WriteNullableDecimal(BinaryWriter writer, decimal? value)
		{
			writer.Write(value.HasValue);
			if (value.HasValue)
			{
				writer.Write(value.Value);
			}
		}

		private static void WriteNullableDouble(BinaryWriter writer, double? value)
		{
			writer.Write(value.HasValue);
			if (value.HasValue)
			{
				writer.Write(value.Value);
			}
		}

		private static void WriteNullableInt(BinaryWriter writer, int? value)
		{
			writer.Write(value.HasValue);
			if (value.HasValue)
			{
				writer.Write(value.Value);
			}
		}

		private static TimeData? ReadNullableTimeData(BinaryReader reader)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (reader.ReadBoolean())
			{
				return TimeData.Deserialize(reader.ReadString());
			}
			return null;
		}

		private static decimal? ReadNullableDecimal(BinaryReader reader)
		{
			if (reader.ReadBoolean())
			{
				return reader.ReadDecimal();
			}
			return null;
		}

		private static double? ReadNullableDouble(BinaryReader reader)
		{
			if (reader.ReadBoolean())
			{
				return reader.ReadDouble();
			}
			return null;
		}

		private static int? ReadNullableInt(BinaryReader reader)
		{
			if (reader.ReadBoolean())
			{
				return reader.ReadInt32();
			}
			return null;
		}
	}
	internal sealed class CsvConverter : IDataConverter
	{
		private CsvConverter()
		{
		}

		internal static CsvConverter Create()
		{
			return new CsvConverter();
		}

		public void Save(List<StockDataIO.StockDataDTO> data, MersenneTwister random, string path, bool simulation = false)
		{
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			using StreamWriter streamWriter = new StreamWriter(path, new FileStreamOptions
			{
				Mode = FileMode.Create,
				Access = FileAccess.Write,
				Share = FileShare.Write
			});
			var (value, uintArray) = random.SaveState();
			streamWriter.WriteLine($"{value}|{Convert.ToBase64String(ConvertUIntArrayToBytes(uintArray))}");
			streamWriter.WriteLine(data[0].TradeSaveData.ToJson());
			streamWriter.WriteLine(JsonSerializer.Serialize(data[1].Articles, new JsonSerializerOptions
			{
				WriteIndented = false
			}));
			string text = "Id,Name,Symbol,Date,Price,Open,Close,High,Low,Volatility,TrendPercentage,TrendStartPrice,TrendEndPrice,TrendSteps,Average";
			if (simulation)
			{
				text += ",OriginalPrice,SimulationChange";
			}
			streamWriter.WriteLine(text);
			for (int i = 2; i < data.Count; i++)
			{
				StockDataIO.StockDataDTO stockDataDTO = data[i];
				DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(14, 15);
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Id);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(EscapeCsvField(stockDataDTO.Name));
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(EscapeCsvField(stockDataDTO.Symbol));
				defaultInterpolatedStringHandler.AppendLiteral(",");
				string value2;
				if (!stockDataDTO.Date.HasValue)
				{
					value2 = string.Empty;
				}
				else
				{
					TimeData value3 = stockDataDTO.Date.Value;
					value2 = EscapeCsvField(((TimeData)(ref value3)).Serialize());
				}
				defaultInterpolatedStringHandler.AppendFormatted(value2);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Price?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Open.ToString(CultureInfo.InvariantCulture));
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Close.HasValue ? stockDataDTO.Close.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.High.ToString(CultureInfo.InvariantCulture));
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Low.ToString(CultureInfo.InvariantCulture));
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Volatility.HasValue ? stockDataDTO.Volatility.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.TrendPercentage.HasValue ? stockDataDTO.TrendPercentage.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.TrendStartPrice.HasValue ? stockDataDTO.TrendStartPrice.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.TrendEndPrice.HasValue ? stockDataDTO.TrendEndPrice.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.TrendSteps.HasValue ? stockDataDTO.TrendSteps.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
				defaultInterpolatedStringHandler.AppendLiteral(",");
				defaultInterpolatedStringHandler.AppendFormatted(stockDataDTO.Average.ToString(CultureInfo.InvariantCulture));
				string text2 = defaultInterpolatedStringHandler.ToStringAndClear();
				if (simulation && stockDataDTO.OriginalPrice.HasValue && stockDataDTO.Price.HasValue)
				{
					text2 = text2 + "," + stockDataDTO.OriginalPrice.Value.ToString(CultureInfo.InvariantCulture);
					text2 = text2 + "," + stockDataDTO.SimulationChange.ToString(CultureInfo.InvariantCulture);
				}
				streamWriter.WriteLine(text2);
			}
		}

		public List<StockDataIO.StockDataDTO> Load(string path)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			List<StockDataIO.StockDataDTO> list = new List<StockDataIO.StockDataDTO>();
			using StreamReader streamReader = new StreamReader(path, new FileStreamOptions
			{
				Mode = FileMode.Open,
				Access = FileAccess.Read,
				Share = FileShare.Read
			});
			string[] array = streamReader.ReadLine().Split('|');
			int item = int.Parse(array[0]);
			uint[] item2 = ConvertByteArrayToUIntArray(Convert.FromBase64String(array[1]));
			MathHelper.Init(new MersenneTwister((item, item2)));
			TradeSaveData tradeSaveData = TradeSaveData.FromJson(streamReader.ReadLine());
			list.Add(new StockDataIO.StockDataDTO
			{
				TradeSaveData = tradeSaveData
			});
			List<Article> articles = JsonSerializer.Deserialize<List<Article>>(streamReader.ReadLine());
			list.Add(new StockDataIO.StockDataDTO
			{

Bogus.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Bogus.Bson;
using Bogus.DataSets;
using Bogus.Extensions;
using Bogus.Extensions.Extras;
using Bogus.Platform;
using Bogus.Vendor;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyProduct("Bogus")]
[assembly: AssemblyTitle("Bogus Fake Data Generator for .NET")]
[assembly: AssemblyCompany("Brian Chavez")]
[assembly: AssemblyCopyright("Brian Chavez © 2023")]
[assembly: AssemblyFileVersion("35.2.0")]
[assembly: AssemblyInformationalVersion("35.2.0 built on 2023-12-26 20:42:03Z")]
[assembly: AssemblyTrademark("MIT License")]
[assembly: AssemblyDescription("https://github.com/bchavez/Bogus")]
[assembly: InternalsVisibleTo("Bogus.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d981a0be5616f450cc0528f88cf96e7bb782edb8ea9a06517cc42b340cdd644a931aab0c7c4902c129eff3d09a3f8c331026286d55c36f225f33f4709be9352fdf16c7b6781652528c2b77063f19a6ab21ee79368f552f60da8f503832e2d6ab6aafce934cc8736c24fd391ac286aa14dd5b5959fa546eb25841e41cfb625aa2")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("35.2.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace System
{
	internal static class AssemblyVersionInformation
	{
		internal const string AssemblyProduct = "Bogus";

		internal const string AssemblyTitle = "Bogus Fake Data Generator for .NET";

		internal const string AssemblyCompany = "Brian Chavez";

		internal const string AssemblyCopyright = "Brian Chavez © 2023";

		internal const string AssemblyVersion = "35.2.0";

		internal const string AssemblyFileVersion = "35.2.0";

		internal const string AssemblyInformationalVersion = "35.2.0 built on 2023-12-26 20:42:03Z";

		internal const string AssemblyTrademark = "MIT License";

		internal const string AssemblyDescription = "https://github.com/bchavez/Bogus";

		internal const string InternalsVisibleTo = "Bogus.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d981a0be5616f450cc0528f88cf96e7bb782edb8ea9a06517cc42b340cdd644a931aab0c7c4902c129eff3d09a3f8c331026286d55c36f225f33f4709be9352fdf16c7b6781652528c2b77063f19a6ab21ee79368f552f60da8f503832e2d6ab6aafce934cc8736c24fd391ac286aa14dd5b5959fa546eb25841e41cfb625aa2";
	}
}
namespace Bogus
{
	public interface IBinder
	{
		Dictionary<string, MemberInfo> GetMembers(Type t);
	}
	public class Binder : IBinder
	{
		protected internal BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public Binder()
		{
		}

		public Binder(BindingFlags bindingFlags)
		{
			BindingFlags = bindingFlags;
		}

		public virtual Dictionary<string, MemberInfo> GetMembers(Type t)
		{
			return (from mi in (from m in t.GetAllMembers(BindingFlags)
					select UseBaseTypeDeclaredPropertyInfo(t, m)).Where(delegate(MemberInfo m)
				{
					if (m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: true).Any())
					{
						return false;
					}
					if (m is PropertyInfo propertyInfo)
					{
						return propertyInfo.CanWrite;
					}
					return m is FieldInfo fieldInfo && !fieldInfo.IsPrivate;
				})
				group mi by mi.Name).ToDictionary((IGrouping<string, MemberInfo> k) => k.Key, (IGrouping<string, MemberInfo> g) => g.First());
		}

		protected virtual MemberInfo UseBaseTypeDeclaredPropertyInfo(Type t, MemberInfo m)
		{
			if (m is PropertyInfo propertyInfo && !propertyInfo.CanWrite && (object)m.DeclaringType != null && m.DeclaringType != t)
			{
				PropertyInfo property = m.DeclaringType.GetProperty(m.Name, BindingFlags);
				if ((object)property != null)
				{
					return property;
				}
			}
			return m;
		}
	}
	public class BogusException : Exception
	{
		public BogusException()
		{
		}

		public BogusException(string message)
			: base(message)
		{
		}

		public BogusException(string message, Exception innerException)
			: base(message, innerException)
		{
		}
	}
	public static class Chars
	{
		public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";

		public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

		public const string Numbers = "0123456789";

		public const string HexLowerCase = "0123456789abcdef";

		public const string HexUpperCase = "0123456789ABCDEF";

		public const string AlphaNumericUpperCase = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

		public const string AlphaNumericLowerCase = "0123456789abcdefghijklmnopqrstuvwxyz";
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	public static class SafeUnicodeRanges
	{
		public static string[] Basic = new string[262]
		{
			"0-9", "A-Z", "a-z", "À-Ö", "Ø-ö", "ø-ˁ", "ˆ-ˑ", "ˠ-ˤ", "ͺ-ͽ", "Έ-Ί",
			"Ύ-Ρ", "Σ-ώ", "ϐ-ϵ", "Ϸ-ҁ", "Ҋ-ԓ", "Ա-Ֆ", "ա-և", "א-ת", "װ-ײ", "ء-غ",
			"ـ-ي", "٠-٩", "ٮ-ٯ", "ٱ-ۓ", "ۥ-ۦ", "ۮ-ۼ", "ܒ-ܯ", "ݍ-ݭ", "ހ-ޥ", "߀-ߪ",
			"ߴ-ߵ", "ऄ-ह", "क़-ॡ", "०-९", "ॻ-ॿ", "অ-ঌ", "এ-ঐ", "ও-ন", "প-র", "শ-হ",
			"ড়-ঢ়", "য়-ৡ", "০-ৱ", "ਅ-ਊ", "ਏ-ਐ", "ਓ-ਨ", "ਪ-ਰ", "ਲ-ਲ਼", "ਵ-ਸ਼", "ਸ-ਹ",
			"ਖ਼-ੜ", "੦-੯", "ੲ-ੴ", "અ-ઍ", "એ-ઑ", "ઓ-ન", "પ-ર", "લ-ળ", "વ-હ", "ૠ-ૡ",
			"૦-૯", "ଅ-ଌ", "ଏ-ଐ", "ଓ-ନ", "ପ-ର", "ଲ-ଳ", "ଵ-ହ", "ଡ଼-ଢ଼", "ୟ-ୡ", "୦-୯",
			"அ-ஊ", "எ-ஐ", "ஒ-க", "ங-ச", "ஞ-ட", "ண-த", "ந-ப", "ம-ஹ", "௦-௯", "అ-ఌ",
			"ఎ-ఐ", "ఒ-న", "ప-ళ", "వ-హ", "ౠ-ౡ", "౦-౯", "ಅ-ಌ", "ಎ-ಐ", "ಒ-ನ", "ಪ-ಳ",
			"ವ-ಹ", "ೠ-ೡ", "೦-೯", "ೱ-ೲ", "അ-ഌ", "എ-ഐ", "ഒ-ന", "പ-ഹ", "ൠ-ൡ", "൦-൯",
			"අ-ඖ", "ක-න", "ඳ-ර", "ව-ෆ", "ก-ะ", "า-ำ", "เ-ๆ", "๐-๙", "ກ-ຂ", "ງ-ຈ",
			"ດ-ທ", "ນ-ຟ", "ມ-ຣ", "ສ-ຫ", "ອ-ະ", "າ-ຳ", "ເ-ໄ", "໐-໙", "ໜ-ໝ", "༠-༩",
			"ཀ-ཇ", "ཉ-ཪ", "ྈ-ྋ", "က-အ", "ဣ-ဧ", "ဩ-ဪ", "၀-၉", "ၐ-ၕ", "Ⴀ-Ⴥ", "ა-ჺ",
			"ᄀ-ᅙ", "ᅟ-ᆢ", "ᆨ-ᇹ", "ሀ-ቈ", "ቊ-ቍ", "ቐ-ቖ", "ቚ-ቝ", "በ-ኈ", "ኊ-ኍ", "ነ-ኰ",
			"ኲ-ኵ", "ኸ-ኾ", "ዂ-ዅ", "ወ-ዖ", "ዘ-ጐ", "ጒ-ጕ", "ጘ-ፚ", "ᎀ-ᎏ", "Ꭰ-Ᏼ", "ᐁ-ᙬ",
			"ᙯ-ᙶ", "ᚁ-ᚚ", "ᚠ-ᛪ", "ᜀ-ᜌ", "ᜎ-ᜑ", "ᜠ-ᜱ", "ᝀ-ᝑ", "ᝠ-ᝬ", "ᝮ-ᝰ", "ក-ឳ",
			"០-៩", "᠐-᠙", "ᠠ-ᡷ", "ᢀ-ᢨ", "ᤀ-ᤜ", "᥆-ᥭ", "ᥰ-ᥴ", "ᦀ-ᦩ", "ᦰ-ᧉ", "᧐-᧙",
			"ᨀ-ᨖ", "ᬅ-ᬳ", "ᭅ-ᭋ", "᭐-᭙", "ᴀ-ᶿ", "Ḁ-ẛ", "Ạ-ỹ", "ἀ-ἕ", "Ἐ-Ἕ", "ἠ-ὅ",
			"Ὀ-Ὅ", "ὐ-ὗ", "Ὗ-ώ", "ᾀ-ᾴ", "ᾶ-ᾼ", "ῂ-ῄ", "ῆ-ῌ", "ῐ-ΐ", "ῖ-Ί", "ῠ-Ῥ",
			"ῲ-ῴ", "ῶ-ῼ", "ₐ-ₔ", "ℊ-ℓ", "ℙ-ℝ", "K-ℭ", "ℯ-ℹ", "ℼ-ℿ", "ⅅ-ⅉ", "Ↄ-ↄ",
			"Ⰰ-Ⱞ", "ⰰ-ⱞ", "Ⱡ-ⱬ", "ⱴ-ⱷ", "Ⲁ-ⳤ", "ⴀ-ⴥ", "ⴰ-ⵥ", "ⶀ-ⶖ", "ⶠ-ⶦ", "ⶨ-ⶮ",
			"ⶰ-ⶶ", "ⶸ-ⶾ", "ⷀ-ⷆ", "ⷈ-ⷎ", "ⷐ-ⷖ", "ⷘ-ⷞ", "々-〆", "〱-〵", "〻-〼", "ぁ-ゖ",
			"ゝ-ゟ", "ァ-ヺ", "ー-ヿ", "ㄅ-ㄬ", "ㄱ-ㆎ", "ㆠ-ㆷ", "ㇰ-ㇿ", "㐀-䶵", "一-龻", "ꀀ-ꒌ",
			"ꜗ-ꜚ", "ꠀ-ꠁ", "ꠃ-ꠅ", "ꠇ-ꠊ", "ꠌ-ꠢ", "ꡀ-ꡳ", "가-힣", "豈-鶴", "侮-頻", "並-龎",
			"ff-st", "ﬓ-ﬗ", "ײַ-ﬨ", "שׁ-זּ", "טּ-לּ", "נּ-סּ", "ףּ-פּ", "צּ-ﮱ", "ﯓ-ﴽ", "ﵐ-ﶏ",
			"ﶒ-ﷇ", "ﷰ-ﷻ", "ﹰ-ﹴ", "ﹶ-ﻼ", "0-9", "A-Z", "a-z", "ヲ-ᄒ", "ᅡ-ᅦ", "ᅧ-ᅬ",
			"ᅭ-ᅲ", "ᅳ-ᅵ"
		};

		public static string[] SurrogatePairs = new string[93]
		{
			"\ud800\udc00-\ud800\udc0b", "\ud800\udc0d-\ud800\udc26", "\ud800\udc28-\ud800\udc3a", "\ud800\udc3c-\ud800\udc3d", "\ud800\udc3f-\ud800\udc4d", "\ud800\udc50-\ud800\udc5d", "\ud800\udc80-\ud800\udcfa", "\ud800\udf00-\ud800\udf1e", "\ud800\udf30-\ud800\udf40", "\ud800\udf42-\ud800\udf49",
			"\ud800\udf80-\ud800\udf9d", "\ud800\udfa0-\ud800\udfc3", "\ud800\udfc8-\ud800\udfcf", "\ud801\udc00-\ud801\udc9d", "\ud801\udca0-\ud801\udca9", "\ud802\udc00-\ud802\udc05", "\ud802\udc0a-\ud802\udc35", "\ud802\udc37-\ud802\udc38", "\ud802\udd00-\ud802\udd15", "\ud802\ude10-\ud802\ude13",
			"\ud802\ude15-\ud802\ude17", "\ud802\ude19-\ud802\ude33", "\ud808\udc00-\ud808\udf6e", "\ud835\udc00-\ud835\udc54", "\ud835\udc56-\ud835\udc9c", "\ud835\udc9e-\ud835\udc9f", "\ud835\udca5-\ud835\udca6", "\ud835\udca9-\ud835\udcac", "\ud835\udcae-\ud835\udcb9", "\ud835\udcbd-\ud835\udcc3",
			"\ud835\udcc5-\ud835\udd05", "\ud835\udd07-\ud835\udd0a", "\ud835\udd0d-\ud835\udd14", "\ud835\udd16-\ud835\udd1c", "\ud835\udd1e-\ud835\udd39", "\ud835\udd3b-\ud835\udd3e", "\ud835\udd40-\ud835\udd44", "\ud835\udd4a-\ud835\udd50", "\ud835\udd52-\ud835\udea5", "\ud835\udea8-\ud835\udec0",
			"\ud835\udec2-\ud835\udeda", "\ud835\udedc-\ud835\udefa", "\ud835\udefc-\ud835\udf14", "\ud835\udf16-\ud835\udf34", "\ud835\udf36-\ud835\udf4e", "\ud835\udf50-\ud835\udf6e", "\ud835\udf70-\ud835\udf88", "\ud835\udf8a-\ud835\udfa8", "\ud835\udfaa-\ud835\udfc2", "\ud835\udfc4-\ud835\udfcb",
			"\ud835\udfce-\ud835\udfff", "\ud840\udc00-\ud840\udfff", "\ud841\udc00-\ud841\udfff", "\ud842\udc00-\ud842\udfff", "\ud843\udc00-\ud843\udfff", "\ud844\udc00-\ud844\udfff", "\ud845\udc00-\ud845\udfff", "\ud846\udc00-\ud846\udfff", "\ud847\udc00-\ud847\udfff", "\ud848\udc00-\ud848\udfff",
			"\ud849\udc00-\ud849\udfff", "\ud84a\udc00-\ud84a\udfff", "\ud84b\udc00-\ud84b\udfff", "\ud84c\udc00-\ud84c\udfff", "\ud84d\udc00-\ud84d\udfff", "\ud84e\udc00-\ud84e\udfff", "\ud84f\udc00-\ud84f\udfff", "\ud850\udc00-\ud850\udfff", "\ud851\udc00-\ud851\udfff", "\ud852\udc00-\ud852\udfff",
			"\ud853\udc00-\ud853\udfff", "\ud854\udc00-\ud854\udfff", "\ud855\udc00-\ud855\udfff", "\ud856\udc00-\ud856\udfff", "\ud857\udc00-\ud857\udfff", "\ud858\udc00-\ud858\udfff", "\ud859\udc00-\ud859\udfff", "\ud85a\udc00-\ud85a\udfff", "\ud85b\udc00-\ud85b\udfff", "\ud85c\udc00-\ud85c\udfff",
			"\ud85d\udc00-\ud85d\udfff", "\ud85e\udc00-\ud85e\udfff", "\ud85f\udc00-\ud85f\udfff", "\ud860\udc00-\ud860\udfff", "\ud861\udc00-\ud861\udfff", "\ud862\udc00-\ud862\udfff", "\ud863\udc00-\ud863\udfff", "\ud864\udc00-\ud864\udfff", "\ud865\udc00-\ud865\udfff", "\ud866\udc00-\ud866\udfff",
			"\ud867\udc00-\ud867\udfff", "\ud868\udc00-\ud868\udfff", "\ud869\udc00-\ud869\uded6"
		};
	}
	public static class Database
	{
		public static Lazy<ConcurrentDictionary<string, BObject>> Data = new Lazy<ConcurrentDictionary<string, BObject>>(Initialize, LazyThreadSafetyMode.ExecutionAndPublication);

		public const string ResourceNameFormat = "Bogus.data.{0}.locale.bson";

		public static string[] GetAllLocales()
		{
			Assembly assembly = typeof(Database).GetAssembly();
			string[] array = "Bogus.data.{0}.locale.bson".Split(new string[1] { "{0}" }, StringSplitOptions.None);
			string prefix = array[0];
			string suffix = array[1];
			return (from name in assembly.GetManifestResourceNames()
				where name.EndsWith(suffix)
				select name.Replace(prefix, "").Replace(suffix, "")).ToArray();
		}

		public static bool LocaleResourceExists(string locale)
		{
			Assembly assembly = typeof(Database).GetAssembly();
			string localeResourceName = GetLocaleResourceName(locale);
			return ResourceHelper.ResourceExists(assembly, localeResourceName);
		}

		private static string GetLocaleResourceName(string locale)
		{
			return $"Bogus.data.{locale}.locale.bson";
		}

		private static ConcurrentDictionary<string, BObject> Initialize()
		{
			ConcurrentDictionary<string, BObject> concurrentDictionary = new ConcurrentDictionary<string, BObject>();
			concurrentDictionary.TryAdd("en", DeserializeLocale("en"));
			return concurrentDictionary;
		}

		internal static BObject DeserializeLocale(string locale)
		{
			Assembly assembly = typeof(Database).GetAssembly();
			string localeResourceName = GetLocaleResourceName(locale);
			return ResourceHelper.ReadBObjectResource(assembly, localeResourceName);
		}

		public static BObject GetLocale(string locale)
		{
			if (!Data.Value.TryGetValue(locale, out var value))
			{
				value = DeserializeLocale(locale);
				Data.Value.TryAdd(locale, value);
			}
			return value;
		}

		public static void ResetLocale(string locale)
		{
			Data.Value[locale] = DeserializeLocale(locale);
		}

		public static bool HasKey(string category, string path, string locale, string fallbackLocale = "en")
		{
			BObject locale2 = GetLocale(locale);
			if (Select(category, path, locale2) != null)
			{
				return true;
			}
			if (fallbackLocale == null)
			{
				return false;
			}
			locale2 = GetLocale(fallbackLocale);
			if (Select(category, path, locale2) != null)
			{
				return true;
			}
			return false;
		}

		public static BValue Get(string category, string path, string locale = "en", string localeFallback = "en")
		{
			BObject locale2 = GetLocale(locale);
			BValue bValue = Select(category, path, locale2);
			if (bValue != null)
			{
				return bValue;
			}
			BObject locale3 = GetLocale(localeFallback);
			return Select(category, path, locale3);
		}

		private static BValue Select(string category, string path, BValue localeRoot)
		{
			BValue bValue = localeRoot[category];
			if ((object)bValue == null)
			{
				return null;
			}
			int startIndex = 0;
			while (true)
			{
				int num = path.IndexOf('.', startIndex);
				string key;
				if (num < 0)
				{
					key = path.Substring(startIndex);
					return bValue[key];
				}
				key = path.Substring(startIndex, num);
				bValue = bValue[key];
				if ((object)bValue == null)
				{
					break;
				}
				startIndex = num + 1;
			}
			return null;
		}
	}
	[AttributeUsage(AttributeTargets.Class)]
	public class DataCategoryAttribute : Attribute
	{
		public string Name { get; set; }

		public DataCategoryAttribute(string name)
		{
			Name = name;
		}
	}
	public class DataSet : ILocaleAware, IHasRandomizer
	{
		protected SeedNotifier Notifier = new SeedNotifier();

		private Randomizer randomizer;

		private static readonly Regex parseTokensRegex = new Regex("\\#{(.*?)\\}", RegexOptions.Compiled);

		protected string Category { get; set; }

		public string Locale { get; set; }

		public Randomizer Random
		{
			get
			{
				return this.randomizer ?? (Random = new Randomizer());
			}
			set
			{
				randomizer = value;
				Notifier.Notify(value);
			}
		}

		public DataSet(string locale = "en")
		{
			if (!Database.LocaleResourceExists(locale))
			{
				throw new BogusException($"The locale '{locale}' does not exist. To see all available locales visit {"https://github.com/bchavez/Bogus"}.");
			}
			Locale = locale;
			Category = ResolveCategory(GetType());
		}

		SeedNotifier IHasRandomizer.GetNotifier()
		{
			return Notifier;
		}

		public static string ResolveCategory(Type type)
		{
			DataCategoryAttribute customAttributeX = type.GetCustomAttributeX<DataCategoryAttribute>();
			if (customAttributeX == null)
			{
				return type.Name.ToLowerInvariant();
			}
			return customAttributeX.Name;
		}

		protected internal virtual BValue Get(string path)
		{
			return Database.Get(Category, path, Locale);
		}

		protected internal virtual BValue Get(string category, string path)
		{
			return Database.Get(category, path, Locale);
		}

		protected internal virtual bool HasKey(string path, bool includeFallback = true)
		{
			if (includeFallback)
			{
				return Database.HasKey(Category, path, Locale);
			}
			return Database.HasKey(Category, path, Locale, null);
		}

		protected internal virtual BArray GetArray(string path)
		{
			return (BArray)Get(path);
		}

		protected internal virtual BArray GetArray(string category, string path)
		{
			return (BArray)Get(category, path);
		}

		protected internal virtual BObject GetObject(string path)
		{
			return (BObject)Get(path);
		}

		protected internal virtual string GetRandomArrayItem(string path, int? min = null, int? max = null)
		{
			return GetRandomArrayItem(Category, path, min, max);
		}

		protected internal virtual string GetRandomArrayItem(string category, string path, int? min = null, int? max = null)
		{
			BArray array = GetArray(category, path);
			if (!array.HasValues)
			{
				return string.Empty;
			}
			return Random.ArrayElement(array, min, max);
		}

		protected internal virtual BObject GetRandomBObject(string path)
		{
			BArray array = GetArray(path);
			if (!array.HasValues)
			{
				return null;
			}
			return Random.ArrayElement(array) as BObject;
		}

		protected internal virtual string GetFormattedValue(string path)
		{
			string randomArrayItem = GetRandomArrayItem(path);
			string format = ParseTokens(randomArrayItem);
			return Random.Replace(format);
		}

		private string ParseTokens(string value)
		{
			return parseTokensRegex.Replace(value, delegate(Match x)
			{
				string[] array = x.Groups[1].Value.ToLowerInvariant().Split('.');
				BArray props = ((array.Length != 1) ? ((BArray)Database.Get(array[0], array[1], Locale)) : ((BArray)Database.Get(Category, array[0], Locale)));
				BValue bValue = Random.ArrayElement(props);
				return ParseTokens(bValue);
			});
		}
	}
	public class Faker : ILocaleAware, IHasRandomizer, IHasContext
	{
		public static bool DefaultStrictMode = false;

		protected SeedNotifier Notifier = new SeedNotifier();

		private Randomizer randomizer;

		private Person person;

		public static int GlobalUniqueIndex = -1;

		private int capturedGlobalIndex;

		public int IndexFaker = -1;

		public int IndexVariable;

		Dictionary<string, object> IHasContext.Context { get; } = new Dictionary<string, object>();


		[RegisterMustasheMethods]
		public Randomizer Random
		{
			get
			{
				return this.randomizer ?? (Random = new Randomizer());
			}
			set
			{
				randomizer = value;
				Notifier.Notify(value);
			}
		}

		public Person Person => person ?? (person = new Person(Random, Locale));

		[RegisterMustasheMethods]
		public Hacker Hacker { get; set; }

		[RegisterMustasheMethods]
		public PhoneNumbers Phone { get; set; }

		[RegisterMustasheMethods]
		public Name Name { get; set; }

		[RegisterMustasheMethods]
		public Lorem Lorem { get; set; }

		[RegisterMustasheMethods]
		public Images Image { get; set; }

		[RegisterMustasheMethods]
		public Finance Finance { get; set; }

		[RegisterMustasheMethods]
		public Address Address { get; set; }

		[RegisterMustasheMethods]
		public Date Date { get; set; }

		[RegisterMustasheMethods]
		public Company Company { get; set; }

		[RegisterMustasheMethods]
		public Internet Internet { get; set; }

		[RegisterMustasheMethods]
		public Commerce Commerce { get; set; }

		[RegisterMustasheMethods]
		public Bogus.DataSets.System System { get; set; }

		[RegisterMustasheMethods]
		public Bogus.DataSets.Database Database { get; set; }

		[RegisterMustasheMethods]
		public Rant Rant { get; set; }

		[RegisterMustasheMethods]
		public Vehicle Vehicle { get; set; }

		[RegisterMustasheMethods]
		public Music Music { get; set; }

		public string Locale { get; set; }

		internal bool HasContext => IndexFaker != -1;

		public int UniqueIndex => capturedGlobalIndex;

		public int IndexGlobal => capturedGlobalIndex;

		public Hashids Hashids { get; set; }

		public Faker(string locale = "en")
		{
			Locale = locale;
			Address = Notifier.Flow(new Address(locale));
			Company = Notifier.Flow(new Company(locale));
			Date = Notifier.Flow(new Date(locale));
			Finance = Notifier.Flow(new Finance
			{
				Locale = locale
			});
			Hacker = Notifier.Flow(new Hacker(locale));
			Image = Notifier.Flow(new Images(locale));
			Internet = Notifier.Flow(new Internet(locale));
			Lorem = Notifier.Flow(new Lorem(locale));
			Name = Notifier.Flow(new Name(locale));
			Phone = Notifier.Flow(new PhoneNumbers(locale));
			System = Notifier.Flow(new Bogus.DataSets.System(locale));
			Commerce = Notifier.Flow(new Commerce(locale));
			Database = Notifier.Flow(new Bogus.DataSets.Database());
			Rant = Notifier.Flow(new Rant());
			Vehicle = Notifier.Flow(new Vehicle());
			Music = Notifier.Flow(new Music());
			Hashids = new Hashids();
		}

		SeedNotifier IHasRandomizer.GetNotifier()
		{
			return Notifier;
		}

		public string Parse(string str)
		{
			return Tokenizer.Parse(str, Address, Company, Date, Finance, Hacker, Image, Internet, Lorem, Name, Phone, System, Commerce, Database, Random);
		}

		public T PickRandom<T>(IEnumerable<T> items)
		{
			return Random.ArrayElement(items.ToArray());
		}

		public T PickRandom<T>(IList<T> items)
		{
			return Random.ListItem(items);
		}

		public T PickRandom<T>(ICollection<T> items)
		{
			return Random.CollectionItem(items);
		}

		public T PickRandom<T>(List<T> items)
		{
			return Random.ListItem(items);
		}

		public T PickRandom<T>(params T[] items)
		{
			return Random.ArrayElement(items);
		}

		public T PickRandomParam<T>(params T[] items)
		{
			return Random.ArrayElement(items);
		}

		public IEnumerable<T> PickRandom<T>(IEnumerable<T> items, int amountToPick)
		{
			if (amountToPick < 0)
			{
				throw new ArgumentOutOfRangeException("amountToPick needs to be a positive integer.");
			}
			int num = items.Count();
			if (amountToPick > num)
			{
				throw new ArgumentOutOfRangeException("amountToPick is greater than the number of items.");
			}
			return Random.Shuffle(items).Take(amountToPick);
		}

		public IList<T> Make<T>(int count, Func<T> action)
		{
			return (from n in Enumerable.Range(1, count)
				select action()).ToList();
		}

		public IList<T> Make<T>(int count, Func<int, T> action)
		{
			return Enumerable.Range(1, count).Select(action).ToList();
		}

		public IEnumerable<T> MakeLazy<T>(int count, Func<T> action)
		{
			return from n in Enumerable.Range(1, count)
				select action();
		}

		public IEnumerable<T> MakeLazy<T>(int count, Func<int, T> action)
		{
			return Enumerable.Range(1, count).Select(action);
		}

		public T PickRandom<T>() where T : struct, Enum
		{
			return Random.Enum(Array.Empty<T>());
		}

		public T PickRandomWithout<T>(params T[] exclude) where T : struct, Enum
		{
			return Random.Enum(exclude);
		}

		internal void NewContext()
		{
			person = null;
			capturedGlobalIndex = Interlocked.Increment(ref GlobalUniqueIndex);
			Interlocked.Increment(ref IndexFaker);
		}
	}
	public interface IFakerTInternal
	{
		Faker FakerHub { get; }

		IBinder Binder { get; }

		int? LocalSeed { get; }

		Type TypeOfT { get; }
	}
	public class Faker<T> : IFakerTInternal, ILocaleAware, IRuleSet<T> where T : class
	{
		protected const string Default = "default";

		private static readonly string[] DefaultRuleSet = new string[1] { "default" };

		protected internal Faker FakerHub;

		protected internal IBinder binder;

		protected internal readonly MultiDictionary<string, string, PopulateAction<T>> Actions = new MultiDictionary<string, string, PopulateAction<T>>(StringComparer.OrdinalIgnoreCase);

		protected internal readonly Dictionary<string, FinalizeAction<T>> FinalizeActions = new Dictionary<string, FinalizeAction<T>>(StringComparer.OrdinalIgnoreCase);

		protected internal Dictionary<string, Func<Faker, T>> CreateActions = new Dictionary<string, Func<Faker, T>>(StringComparer.OrdinalIgnoreCase);

		protected internal readonly Dictionary<string, MemberInfo> TypeProperties;

		protected internal readonly Dictionary<string, Action<T, object>> SetterCache = new Dictionary<string, Action<T, object>>(StringComparer.OrdinalIgnoreCase);

		protected internal Dictionary<string, bool> StrictModes = new Dictionary<string, bool>();

		protected internal bool? IsValid;

		protected internal string currentRuleSet = "default";

		protected internal int? localSeed;

		private readonly object _setterCreateLock = new object();

		Faker IFakerTInternal.FakerHub => FakerHub;

		IBinder IFakerTInternal.Binder => binder;

		int? IFakerTInternal.LocalSeed => localSeed;

		Type IFakerTInternal.TypeOfT => typeof(T);

		public string Locale { get; set; }

		public Faker<T> Clone()
		{
			Faker<T> faker = new Faker<T>(Locale, binder);
			foreach (KeyValuePair<string, bool> strictMode in StrictModes)
			{
				faker.StrictModes.Add(strictMode.Key, strictMode.Value);
			}
			foreach (KeyValuePair<string, Func<Faker, T>> createAction in CreateActions)
			{
				faker.CreateActions[createAction.Key] = createAction.Value;
			}
			foreach (KeyValuePair<string, FinalizeAction<T>> finalizeAction in FinalizeActions)
			{
				faker.FinalizeActions.Add(finalizeAction.Key, finalizeAction.Value);
			}
			foreach (KeyValuePair<string, Dictionary<string, PopulateAction<T>>> action in Actions)
			{
				foreach (KeyValuePair<string, PopulateAction<T>> item in action.Value)
				{
					faker.Actions.Add(action.Key, item.Key, item.Value);
				}
			}
			if (localSeed.HasValue)
			{
				faker.UseSeed(localSeed.Value);
			}
			return faker;
		}

		public Faker()
			: this("en", (IBinder)null)
		{
		}

		public Faker(string locale)
			: this(locale, (IBinder)null)
		{
		}

		public Faker(string locale = "en", IBinder binder = null)
		{
			this.binder = binder ?? new Binder();
			Locale = locale;
			FakerHub = new Faker(locale);
			TypeProperties = this.binder.GetMembers(typeof(T));
			CreateActions["default"] = (Faker faker) => Activator.CreateInstance<T>();
		}

		public virtual Faker<T> UseSeed(int seed)
		{
			localSeed = seed;
			FakerHub.Random = new Randomizer(seed);
			return this;
		}

		public virtual Faker<T> CustomInstantiator(Func<Faker, T> factoryMethod)
		{
			CreateActions[currentRuleSet] = factoryMethod;
			return this;
		}

		public virtual Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<Faker, T, TProperty> setter)
		{
			string propertyOrField = PropertyName.For(property);
			return AddRule(propertyOrField, (Faker f, T t) => setter(f, t));
		}

		public virtual Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, TProperty value)
		{
			string propertyOrField = PropertyName.For(property);
			return AddRule(propertyOrField, (Faker f, T t) => value);
		}

		public virtual Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<TProperty> valueFunction)
		{
			string propertyOrField = PropertyName.For(property);
			return AddRule(propertyOrField, (Faker f, T t) => valueFunction());
		}

		public virtual Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<Faker, TProperty> setter)
		{
			string propertyOrField = PropertyName.For(property);
			return AddRule(propertyOrField, (Faker f, T t) => setter(f));
		}

		public virtual Faker<T> RuleFor<TProperty>(string propertyOrFieldName, Func<Faker, TProperty> setter)
		{
			EnsureMemberExists(propertyOrFieldName, $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} cannot be found. Try creating a custom IBinder for Faker<T> with the appropriate System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}.");
			return AddRule(propertyOrFieldName, (Faker f, T t) => setter(f));
		}

		public virtual Faker<T> RuleFor<TProperty>(string propertyOrFieldName, Func<Faker, T, TProperty> setter)
		{
			EnsureMemberExists(propertyOrFieldName, $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} cannot be found. Try creating a custom IBinder for Faker<T> with the appropriate System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}.");
			return AddRule(propertyOrFieldName, (Faker f, T t) => setter(f, t));
		}

		protected virtual Faker<T> AddRule(string propertyOrField, Func<Faker, T, object> invoker)
		{
			PopulateAction<T> value = new PopulateAction<T>
			{
				Action = invoker,
				RuleSet = currentRuleSet,
				PropertyName = propertyOrField
			};
			Actions.Add(currentRuleSet, propertyOrField, value);
			return this;
		}

		public virtual Faker<T> Rules(Action<Faker, T> setActions)
		{
			Func<Faker, T, object> action = delegate(Faker f, T t)
			{
				setActions(f, t);
				return null;
			};
			string text = Guid.NewGuid().ToString();
			PopulateAction<T> value = new PopulateAction<T>
			{
				Action = action,
				RuleSet = currentRuleSet,
				PropertyName = text,
				ProhibitInStrictMode = true
			};
			Actions.Add(currentRuleSet, text, value);
			return this;
		}

		public virtual Faker<T> RuleForType<TType>(Type type, Func<Faker, TType> setterForType)
		{
			if (typeof(TType) != type)
			{
				throw new ArgumentException("TType must be the same type as parameter named 'type'");
			}
			foreach (KeyValuePair<string, MemberInfo> typeProperty in TypeProperties)
			{
				Type fieldOrPropertyType = GetFieldOrPropertyType(typeProperty.Value);
				string key = typeProperty.Key;
				if (fieldOrPropertyType == type)
				{
					RuleFor(key, setterForType);
				}
			}
			return this;
		}

		protected virtual Type GetFieldOrPropertyType(MemberInfo mi)
		{
			if (mi is PropertyInfo propertyInfo)
			{
				return propertyInfo.PropertyType;
			}
			if (mi is FieldInfo fieldInfo)
			{
				return fieldInfo.FieldType;
			}
			return null;
		}

		public virtual Faker<T> RuleSet(string ruleSetName, Action<IRuleSet<T>> action)
		{
			if (currentRuleSet != "default")
			{
				throw new ArgumentException("Cannot create a rule set within a rule set.");
			}
			currentRuleSet = ruleSetName;
			action(this);
			currentRuleSet = "default";
			return this;
		}

		protected virtual void EnsureMemberExists(string propNameOrField, string exceptionMessage)
		{
			if (!TypeProperties.TryGetValue(propNameOrField, out var _))
			{
				throw new ArgumentException(exceptionMessage);
			}
		}

		public virtual Faker<T> Ignore(string propertyOrFieldName)
		{
			EnsureMemberExists(propertyOrFieldName, $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. Can't ignore member {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} cannot be found. Try creating a custom IBinder for Faker<T> with the appropriate System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}.");
			PopulateAction<T> value = new PopulateAction<T>
			{
				Action = null,
				RuleSet = currentRuleSet,
				PropertyName = propertyOrFieldName
			};
			Actions.Add(currentRuleSet, propertyOrFieldName, value);
			return this;
		}

		public virtual Faker<T> Ignore<TPropertyOrField>(Expression<Func<T, TPropertyOrField>> propertyOrField)
		{
			string propertyOrFieldName = PropertyName.For(propertyOrField);
			return Ignore(propertyOrFieldName);
		}

		public virtual Faker<T> StrictMode(bool ensureRulesForAllProperties)
		{
			StrictModes[currentRuleSet] = ensureRulesForAllProperties;
			return this;
		}

		public virtual Faker<T> FinishWith(Action<Faker, T> action)
		{
			FinalizeAction<T> value = new FinalizeAction<T>
			{
				Action = action,
				RuleSet = currentRuleSet
			};
			FinalizeActions[currentRuleSet] = value;
			return this;
		}

		protected virtual string[] ParseDirtyRulesSets(string dirtyRules)
		{
			dirtyRules = dirtyRules?.Trim(',').Trim();
			if (string.IsNullOrWhiteSpace(dirtyRules))
			{
				return DefaultRuleSet;
			}
			return (from s in dirtyRules.Split(',')
				where !string.IsNullOrWhiteSpace(s)
				select s.Trim()).ToArray();
		}

		public virtual T Generate(string ruleSets = null)
		{
			Func<Faker, T> value = null;
			string[] array = ParseDirtyRulesSets(ruleSets);
			if (string.IsNullOrWhiteSpace(ruleSets))
			{
				value = CreateActions["default"];
			}
			else
			{
				string key = array[0];
				value = (CreateActions.TryGetValue(key, out value) ? value : CreateActions["default"]);
			}
			FakerHub.NewContext();
			T val = value(FakerHub);
			PopulateInternal(val, array);
			return val;
		}

		public virtual List<T> Generate(int count, string ruleSets = null)
		{
			return (from i in Enumerable.Range(1, count)
				select Generate(ruleSets)).ToList();
		}

		public virtual IEnumerable<T> GenerateLazy(int count, string ruleSets = null)
		{
			return from i in Enumerable.Range(1, count)
				select Generate(ruleSets);
		}

		public virtual IEnumerable<T> GenerateForever(string ruleSets = null)
		{
			while (true)
			{
				yield return Generate(ruleSets);
			}
		}

		public virtual void Populate(T instance, string ruleSets = null)
		{
			string[] ruleSets2 = ParseDirtyRulesSets(ruleSets);
			PopulateInternal(instance, ruleSets2);
		}

		protected virtual void PopulateInternal(T instance, string[] ruleSets)
		{
			ValidationResult validationResult = null;
			if (!IsValid.HasValue)
			{
				validationResult = ValidateInternal(ruleSets);
				IsValid = validationResult.IsValid;
			}
			if (!IsValid.GetValueOrDefault())
			{
				throw MakeValidationException(validationResult ?? ValidateInternal(ruleSets));
			}
			lock (Randomizer.Locker.Value)
			{
				if (!FakerHub.HasContext)
				{
					FakerHub.NewContext();
				}
				string[] array = ruleSets;
				foreach (string key in array)
				{
					if (!Actions.TryGetValue(key, out var value))
					{
						continue;
					}
					foreach (PopulateAction<T> value3 in value.Values)
					{
						PopulateProperty(instance, value3);
					}
				}
				array = ruleSets;
				foreach (string key2 in array)
				{
					if (FinalizeActions.TryGetValue(key2, out var value2))
					{
						value2.Action(FakerHub, instance);
					}
				}
			}
		}

		private void PopulateProperty(T instance, PopulateAction<T> action)
		{
			Func<Faker, T, object> action2 = action.Action;
			if (action2 == null)
			{
				return;
			}
			object arg = action2(FakerHub, instance);
			if (SetterCache.TryGetValue(action.PropertyName, out var value))
			{
				value(instance, arg);
			}
			else
			{
				if (!TypeProperties.TryGetValue(action.PropertyName, out var value2) || value2 == null)
				{
					return;
				}
				lock (_setterCreateLock)
				{
					if (SetterCache.TryGetValue(action.PropertyName, out value))
					{
						value(instance, arg);
						return;
					}
					if (value2 is PropertyInfo property)
					{
						value = property.CreateSetter<T>();
					}
					else
					{
						FieldInfo field = value2 as FieldInfo;
						if ((object)field != null)
						{
							value = delegate(T i, object v)
							{
								field?.SetValue(i, v);
							};
						}
					}
					if (value != null)
					{
						SetterCache.Add(action.PropertyName, value);
						value(instance, arg);
					}
				}
			}
		}

		public virtual bool Validate(string ruleSets = null)
		{
			string[] ruleSets2 = ((ruleSets == null) ? Actions.Keys.ToArray() : ParseDirtyRulesSets(ruleSets));
			return ValidateInternal(ruleSets2).IsValid;
		}

		public virtual void AssertConfigurationIsValid(string ruleSets = null)
		{
			string[] ruleSets2 = ((ruleSets != null) ? ParseDirtyRulesSets(ruleSets) : Actions.Keys.ToArray());
			ValidationResult validationResult = ValidateInternal(ruleSets2);
			if (!validationResult.IsValid)
			{
				throw MakeValidationException(validationResult);
			}
		}

		protected virtual ValidationException MakeValidationException(ValidationResult result)
		{
			StringBuilder builder = new StringBuilder();
			result.ExtraMessages.ForEach(delegate(string m)
			{
				builder.AppendLine(m);
				builder.AppendLine();
			});
			StringBuilder stringBuilder = builder.AppendLine("Validation was called to ensure all properties / fields have rules.");
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(40, 1, stringBuilder);
			handler.AppendLiteral("There are missing rules for Faker<T> '");
			handler.AppendFormatted(typeof(T).Name);
			handler.AppendLiteral("'.");
			stringBuilder.AppendLine(ref handler).AppendLine("=========== Missing Rules ===========");
			foreach (string missingRule in result.MissingRules)
			{
				builder.AppendLine(missingRule);
			}
			return new ValidationException(builder.ToString().Trim());
		}

		private ValidationResult ValidateInternal(string[] ruleSets)
		{
			ValidationResult validationResult = new ValidationResult
			{
				IsValid = true
			};
			Dictionary<string, MemberInfo>.KeyCollection keys = TypeProperties.Keys;
			foreach (string key in ruleSets)
			{
				if (!StrictModes.TryGetValue(key, out var value))
				{
					value = Faker.DefaultStrictMode;
				}
				if (!value)
				{
					continue;
				}
				Actions.TryGetValue(key, out var value2);
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				if (value2 != null)
				{
					hashSet.UnionWith(value2.Keys);
				}
				hashSet.SymmetricExceptWith(keys);
				if (hashSet.Count <= 0)
				{
					continue;
				}
				foreach (string item in hashSet)
				{
					if (value2 != null && value2.TryGetValue(item, out var value3))
					{
						if (value3.ProhibitInStrictMode)
						{
							validationResult.ExtraMessages.Add($"When StrictMode is set to True the Faker<{typeof(T).Name}>.Rules(...) method cannot verify that all properties have rules. You need to use Faker<{typeof(T).Name}>.RuleFor( x => x.Prop, ...) for each property to ensure each property has an associated rule when StrictMode is true; otherwise, set StrictMode to False in order to use Faker<{typeof(T).Name}>.Rules() method.");
							validationResult.IsValid = false;
						}
					}
					else
					{
						validationResult.MissingRules.Add(item);
						validationResult.IsValid = false;
					}
				}
			}
			return validationResult;
		}

		public static implicit operator T(Faker<T> faker)
		{
			return faker.Generate();
		}

		[Obsolete("This exists here only as a Visual Studio IntelliSense work around. See: https://github.com/bchavez/Bogus/issues/54", true)]
		public void RuleFor<TProperty>(Expression<Func<T, TProperty>> property)
		{
			throw new NotImplementedException();
		}
	}
	public static class ExtensionsForFakerT
	{
		public static List<T> GenerateBetween<T>(this Faker<T> faker, int min, int max, string ruleSets = null) where T : class
		{
			int count = ((IFakerTInternal)faker).FakerHub.Random.Number(min, max);
			return faker.Generate(count, ruleSets);
		}

		public static T OrNull<T>(this T value, in Faker f, float nullWeight = 0.5f) where T : class
		{
			if (nullWeight > 1f || nullWeight < 0f)
			{
				throw new ArgumentOutOfRangeException("nullWeight", $".{"OrNull"}() {"nullWeight"} of '{nullWeight}' must be between 1.0f and 0.0f.");
			}
			if (!(f.Random.Float() > nullWeight))
			{
				return null;
			}
			return value;
		}

		public static T? OrNull<T>(this T value, Faker f, float nullWeight = 0.5f) where T : struct
		{
			if (nullWeight > 1f || nullWeight < 0f)
			{
				throw new ArgumentOutOfRangeException("nullWeight", $".{"OrNull"}() {"nullWeight"} of '{nullWeight}' must be between 1.0f and 0.0f.");
			}
			if (!(f.Random.Float() > nullWeight))
			{
				return null;
			}
			return value;
		}

		public static T OrDefault<T>(this T value, Faker f, float defaultWeight = 0.5f, T defaultValue = default(T))
		{
			if (defaultWeight > 1f || defaultWeight < 0f)
			{
				throw new ArgumentOutOfRangeException("defaultWeight", $".{"OrDefault"}() {"defaultWeight"} of '{defaultWeight}' must be between 1.0f and 0.0f. ");
			}
			if (!(f.Random.Float() > defaultWeight))
			{
				return defaultValue;
			}
			return value;
		}
	}
	public class Hashids : IHashids
	{
		public const string DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

		public const string DEFAULT_SEPS = "cfhistuCFHISTU";

		private const int MIN_ALPHABET_LENGTH = 16;

		private const double SEP_DIV = 3.5;

		private const double GUARD_DIV = 12.0;

		private string alphabet;

		private string salt;

		private string seps;

		private string guards;

		private int minHashLength;

		private Regex guardsRegex;

		private Regex sepsRegex;

		private static Regex hexValidator = new Regex("^[0-9a-fA-F]+$", RegexOptions.Compiled);

		private static Regex hexSplitter = new Regex("[\\w\\W]{1,12}", RegexOptions.Compiled);

		public Hashids()
			: this(string.Empty)
		{
		}

		public Hashids(string salt = "", int minHashLength = 0, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", string seps = "cfhistuCFHISTU")
		{
			if (string.IsNullOrWhiteSpace(alphabet))
			{
				throw new ArgumentNullException("alphabet");
			}
			this.salt = salt;
			this.alphabet = string.Join(string.Empty, alphabet.Distinct());
			this.seps = seps;
			this.minHashLength = minHashLength;
			if (this.alphabet.Length < 16)
			{
				throw new ArgumentException("alphabet must contain at least 4 unique characters.", "alphabet");
			}
			SetupSeps();
			SetupGuards();
		}

		public virtual string Encode(params int[] numbers)
		{
			return GenerateHashFrom(((IEnumerable<int>)numbers).Select((Func<int, long>)((int n) => n)).ToArray());
		}

		public virtual string Encode(IEnumerable<int> numbers)
		{
			return Encode(numbers.ToArray());
		}

		public virtual int[] Decode(string hash)
		{
			return (from n in GetNumbersFrom(hash)
				select (int)n).ToArray();
		}

		public virtual string EncodeHex(string hex)
		{
			if (!hexValidator.IsMatch(hex))
			{
				return string.Empty;
			}
			List<long> list = new List<long>();
			foreach (Match item2 in hexSplitter.Matches(hex))
			{
				long item = Convert.ToInt64("1" + item2.Value, 16);
				list.Add(item);
			}
			return EncodeLong(list.ToArray());
		}

		public virtual string DecodeHex(string hash)
		{
			StringBuilder stringBuilder = new StringBuilder();
			long[] array = DecodeLong(hash);
			foreach (long num in array)
			{
				stringBuilder.Append($"{num:X}".Substring(1));
			}
			return stringBuilder.ToString();
		}

		public long[] DecodeLong(string hash)
		{
			return GetNumbersFrom(hash);
		}

		public string EncodeLong(params long[] numbers)
		{
			return GenerateHashFrom(numbers);
		}

		public string EncodeLong(IEnumerable<long> numbers)
		{
			return EncodeLong(numbers.ToArray());
		}

		[Obsolete("Use 'Encode' instead. The method was renamed to better explain what it actually does.")]
		public virtual string Encrypt(params int[] numbers)
		{
			return Encode(numbers);
		}

		[Obsolete("Use 'EncodeHex' instead. The method was renamed to better explain what it actually does.")]
		public virtual string EncryptHex(string hex)
		{
			return EncodeHex(hex);
		}

		[Obsolete("Use 'Decode' instead. Method was renamed to better explain what it actually does.")]
		public virtual int[] Decrypt(string hash)
		{
			return Decode(hash);
		}

		[Obsolete("Use 'DecodeHex' instead. The method was renamed to better explain what it actually does.")]
		public virtual string DecryptHex(string hash)
		{
			return DecodeHex(hash);
		}

		private void SetupSeps()
		{
			seps = new string(seps.Intersect(alphabet.ToArray()).ToArray());
			alphabet = new string(alphabet.Except(seps.ToArray()).ToArray());
			seps = ConsistentShuffle(seps, salt);
			if (seps.Length == 0 || (double)(alphabet.Length / seps.Length) > 3.5)
			{
				int num = (int)Math.Ceiling((double)alphabet.Length / 3.5);
				if (num == 1)
				{
					num = 2;
				}
				if (num > seps.Length)
				{
					int num2 = num - seps.Length;
					seps += alphabet.Substring(0, num2);
					alphabet = alphabet.Substring(num2);
				}
				else
				{
					seps = seps.Substring(0, num);
				}
			}
			sepsRegex = new Regex("[" + seps + "]", RegexOptions.Compiled);
			alphabet = ConsistentShuffle(alphabet, salt);
		}

		private void SetupGuards()
		{
			int num = (int)Math.Ceiling((double)alphabet.Length / 12.0);
			if (alphabet.Length < 3)
			{
				guards = seps.Substring(0, num);
				seps = seps.Substring(num);
			}
			else
			{
				guards = alphabet.Substring(0, num);
				alphabet = alphabet.Substring(num);
			}
			guardsRegex = new Regex("[" + guards + "]", RegexOptions.Compiled);
		}

		private string GenerateHashFrom(long[] numbers)
		{
			if (numbers == null || numbers.Length == 0)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder();
			string text = alphabet;
			long num = 0L;
			for (int i = 0; i < numbers.Length; i++)
			{
				num += (int)(numbers[i] % (i + 100));
			}
			char c = text[(int)(num % text.Length)];
			stringBuilder.Append(c.ToString());
			for (int j = 0; j < numbers.Length; j++)
			{
				long num2 = numbers[j];
				string text2 = c + salt + text;
				text = ConsistentShuffle(text, text2.Substring(0, text.Length));
				string text3 = Hash(num2, text);
				stringBuilder.Append(text3);
				if (j + 1 < numbers.Length)
				{
					num2 %= text3[0] + j;
					int index = (int)num2 % seps.Length;
					stringBuilder.Append(seps[index]);
				}
			}
			if (stringBuilder.Length < minHashLength)
			{
				int index2 = (int)(num + (int)stringBuilder[0]) % guards.Length;
				char value = guards[index2];
				stringBuilder.Insert(0, value);
				if (stringBuilder.Length < minHashLength)
				{
					index2 = (int)(num + (int)stringBuilder[2]) % guards.Length;
					value = guards[index2];
					stringBuilder.Append(value);
				}
			}
			int num3 = text.Length / 2;
			while (stringBuilder.Length < minHashLength)
			{
				text = ConsistentShuffle(text, text);
				stringBuilder.Insert(0, text.Substring(num3));
				stringBuilder.Append(text.Substring(0, num3));
				int num4 = stringBuilder.Length - minHashLength;
				if (num4 > 0)
				{
					stringBuilder.Remove(0, num4 / 2);
					stringBuilder.Remove(minHashLength, stringBuilder.Length - minHashLength);
				}
			}
			return stringBuilder.ToString();
		}

		private string Hash(long input, string alphabet)
		{
			StringBuilder stringBuilder = new StringBuilder();
			do
			{
				stringBuilder.Insert(0, alphabet[(int)(input % alphabet.Length)]);
				input /= alphabet.Length;
			}
			while (input > 0);
			return stringBuilder.ToString();
		}

		private long Unhash(string input, string alphabet)
		{
			long num = 0L;
			for (int i = 0; i < input.Length; i++)
			{
				int num2 = alphabet.IndexOf(input[i]);
				num += (long)((double)num2 * Math.Pow(alphabet.Length, input.Length - i - 1));
			}
			return num;
		}

		private long[] GetNumbersFrom(string hash)
		{
			if (string.IsNullOrWhiteSpace(hash))
			{
				return new long[0];
			}
			string text = new string(alphabet.ToCharArray());
			List<long> list = new List<long>();
			int num = 0;
			string text2 = guardsRegex.Replace(hash, " ");
			string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 3 || array.Length == 2)
			{
				num = 1;
			}
			text2 = array[num];
			if (text2[0] != 0)
			{
				char c = text2[0];
				text2 = text2.Substring(1);
				text2 = sepsRegex.Replace(text2, " ");
				array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string input in array)
				{
					string text3 = c + salt + text;
					text = ConsistentShuffle(text, text3.Substring(0, text.Length));
					list.Add(Unhash(input, text));
				}
				if (EncodeLong(list.ToArray()) != hash)
				{
					list.Clear();
				}
			}
			return list.ToArray();
		}

		private string ConsistentShuffle(string alphabet, string salt)
		{
			if (string.IsNullOrWhiteSpace(salt))
			{
				return alphabet;
			}
			int num3;
			int num2;
			int num;
			int num4 = (num3 = (num2 = (num = 0)));
			int num5 = alphabet.Length - 1;
			while (num5 > 0)
			{
				num4 %= salt.Length;
				num3 += (num2 = salt[num4]);
				num = (num2 + num4 + num3) % num5;
				char c = alphabet[num];
				alphabet = alphabet.Substring(0, num) + alphabet[num5] + alphabet.Substring(num + 1);
				alphabet = alphabet.Substring(0, num5) + c + alphabet.Substring(num5 + 1);
				num5--;
				num4++;
			}
			return alphabet;
		}
	}
	public interface IHashids
	{
		int[] Decode(string hash);

		long[] DecodeLong(string hash);

		string DecodeHex(string hash);

		string Encode(params int[] numbers);

		string Encode(IEnumerable<int> numbers);

		string EncodeLong(params long[] numbers);

		string EncodeLong(IEnumerable<long> numbers);

		string EncodeHex(string hex);
	}
	public interface ILocaleAware
	{
		string Locale { get; set; }
	}
	public interface IHasContext
	{
		Dictionary<string, object> Context { get; }
	}
	public interface IRuleSet<T> where T : class
	{
		Faker<T> CustomInstantiator(Func<Faker, T> factoryMethod);

		Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<Faker, T, TProperty> setter);

		Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<Faker, TProperty> setter);

		Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, Func<TProperty> valueFunction);

		Faker<T> Ignore<TPropertyOrField>(Expression<Func<T, TPropertyOrField>> propertyOrField);

		Faker<T> StrictMode(bool ensureRulesForAllProperties);

		Faker<T> FinishWith(Action<Faker, T> action);

		Faker<T> RuleFor<TProperty>(Expression<Func<T, TProperty>> property, TProperty value);

		Faker<T> Rules(Action<Faker, T> setActions);
	}
	public class Person : IHasRandomizer, IHasContext
	{
		public class CardAddress
		{
			public class CardGeo
			{
				public double Lat;

				public double Lng;
			}

			public string Street;

			public string Suite;

			public string City;

			public string State;

			public string ZipCode;

			public CardGeo Geo;
		}

		public class CardCompany
		{
			public string Name;

			public string CatchPhrase;

			public string Bs;
		}

		internal Dictionary<string, object> context = new Dictionary<string, object>();

		protected SeedNotifier Notifier = new SeedNotifier();

		private Randomizer randomizer;

		public Name.Gender Gender;

		public string FirstName;

		public string LastName;

		public string FullName;

		public string UserName;

		public string Avatar;

		public string Email;

		public DateTime DateOfBirth;

		public CardAddress Address;

		public string Phone;

		public string Website;

		public CardCompany Company;

		Dictionary<string, object> IHasContext.Context => context;

		protected Name DsName { get; set; }

		protected Internet DsInternet { get; set; }

		protected Date DsDate { get; set; }

		protected PhoneNumbers DsPhoneNumbers { get; set; }

		protected Address DsAddress { get; set; }

		protected Company DsCompany { get; set; }

		public Randomizer Random
		{
			get
			{
				return this.randomizer ?? (Random = new Randomizer());
			}
			set
			{
				randomizer = value;
				Notifier.Notify(value);
			}
		}

		public Person(string locale = "en", int? seed = null)
		{
			GetDataSources(locale);
			if (seed.HasValue)
			{
				Random = new Randomizer(seed.Value);
			}
			Populate();
		}

		internal Person(Randomizer randomizer, string locale = "en")
		{
			GetDataSources(locale);
			Random = randomizer;
			Populate();
		}

		private void GetDataSources(string locale)
		{
			DsName = Notifier.Flow(new Name(locale));
			DsInternet = Notifier.Flow(new Internet(locale));
			DsDate = Notifier.Flow(new Date
			{
				Locale = locale
			});
			DsPhoneNumbers = Notifier.Flow(new PhoneNumbers(locale));
			DsAddress = Notifier.Flow(new Address(locale));
			DsCompany = Notifier.Flow(new Company(locale));
		}

		protected internal virtual void Populate()
		{
			Gender = Random.Enum(Array.Empty<Name.Gender>());
			FirstName = DsName.FirstName(Gender);
			LastName = DsName.LastName(Gender);
			FullName = FirstName + " " + LastName;
			UserName = DsInternet.UserName(FirstName, LastName);
			Email = DsInternet.Email(FirstName, LastName);
			Website = DsInternet.DomainName();
			Avatar = DsInternet.Avatar();
			DateOfBirth = DsDate.Past(50, Date.SystemClock().AddYears(-20));
			Phone = DsPhoneNumbers.PhoneNumber();
			Address = new CardAddress
			{
				Street = DsAddress.StreetAddress(),
				Suite = DsAddress.SecondaryAddress(),
				City = DsAddress.City(),
				State = DsAddress.State(),
				ZipCode = DsAddress.ZipCode(),
				Geo = new CardAddress.CardGeo
				{
					Lat = DsAddress.Latitude(),
					Lng = DsAddress.Longitude()
				}
			};
			Company = new CardCompany
			{
				Name = DsCompany.CompanyName(),
				CatchPhrase = DsCompany.CatchPhrase(),
				Bs = DsCompany.Bs()
			};
		}

		SeedNotifier IHasRandomizer.GetNotifier()
		{
			return Notifier;
		}
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	public static class PropertyName
	{
		public static string For<T, TProp>(Expression<Func<T, TProp>> expression)
		{
			return GetMemberName(expression.Body);
		}

		public static string For<T>(Expression<Func<T, object>> expression)
		{
			return GetMemberName(expression.Body);
		}

		public static string For(Expression<Func<object>> expression)
		{
			return GetMemberName(expression.Body);
		}

		public static string GetMemberName(Expression expression)
		{
			string text = expression.ToString();
			if (text.IndexOf('.') != text.LastIndexOf('.'))
			{
				throw new ArgumentException($"Your expression '{text}' cant be used. Nested accessors like 'o => o.NestedObject.Foo' at a parent level are not allowed. You should create a dedicated faker for NestedObject like new Faker<NestedObject>().RuleFor(o => o.Foo, ...) with its own rules that define how 'Foo' is generated. " + "See this GitHub issue for more info: https://github.com/bchavez/Bogus/issues/115");
			}
			MemberExpression memberExpression = ((!(expression is UnaryExpression unaryExpression)) ? (expression as MemberExpression) : (unaryExpression.Operand as MemberExpression));
			if (memberExpression == null)
			{
				throw new ArgumentException("Expression was not of the form 'x => x.Property or x => x.Field'.");
			}
			return memberExpression.Member.Name;
		}
	}
	public class Randomizer
	{
		public static Random Seed = new Random();

		internal static Lazy<object> Locker = new Lazy<object>(() => new object(), LazyThreadSafetyMode.ExecutionAndPublication);

		protected Random localSeed;

		private WordFunctions wordFunctions;

		private static char[] AlphaChars = new char[36]
		{
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
			'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
			'u', 'v', 'w', 'x', 'y', 'z'
		};

		private static char[] HexChars = new char[16]
		{
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'a', 'b', 'c', 'd', 'e', 'f'
		};

		public Randomizer()
		{
			localSeed = Seed;
		}

		public Randomizer(int localSeed)
		{
			this.localSeed = new Random(localSeed);
		}

		public int Number(int max)
		{
			return Number(0, max);
		}

		public int[] Digits(int count, int minDigit = 0, int maxDigit = 9)
		{
			if (maxDigit > 9 || maxDigit < 0)
			{
				throw new ArgumentException("max digit can't be lager than 9 or smaller than 0", "maxDigit");
			}
			if (minDigit > 9 || minDigit < 0)
			{
				throw new ArgumentException("min digit can't be lager than 9 or smaller than 0", "minDigit");
			}
			int[] array = new int[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = Number(minDigit, maxDigit);
			}
			return array;
		}

		public int Number(int min = 0, int max = 1)
		{
			lock (Locker.Value)
			{
				if (max < int.MaxValue)
				{
					return localSeed.Next(min, max + 1);
				}
				if (min > int.MinValue)
				{
					return 1 + localSeed.Next(min - 1, max);
				}
				int num = localSeed.Next();
				int num2 = localSeed.Next();
				int num3 = (num >> 8) & 0xFFFF;
				int num4 = (num2 >> 8) & 0xFFFF;
				return (num3 << 16) | num4;
			}
		}

		public int Even(int min = 0, int max = 1)
		{
			if (min > max)
			{
				throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", "max");
			}
			if ((min & 1) == 1 && max - 1 < min)
			{
				throw new ArgumentException("The specified range does not contain any even numbers.", "max");
			}
			min = (min + 1) & -2;
			max |= 1;
			if (min > max)
			{
				return min;
			}
			return Number(min, max) & -2;
		}

		public int Odd(int min = 0, int max = 1)
		{
			if (min > max)
			{
				throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", "max");
			}
			if ((max & 1) == 0 && min + 1 > max)
			{
				throw new ArgumentException("The specified range does not contain any odd numbers.", "max");
			}
			if (max == int.MinValue)
			{
				return -2147483647;
			}
			min &= -2;
			max = (max - 1) | 1;
			if (min > max)
			{
				return min | 1;
			}
			return Number(min, max) | 1;
		}

		public double Double(double min = 0.0, double max = 1.0)
		{
			lock (Locker.Value)
			{
				if (min == 0.0 && max == 1.0)
				{
					return localSeed.NextDouble();
				}
				return localSeed.NextDouble() * (max - min) + min;
			}
		}

		public decimal Decimal(decimal min = 0.0m, decimal max = 1.0m)
		{
			return Convert.ToDecimal(Double()) * (max - min) + min;
		}

		public float Float(float min = 0f, float max = 1f)
		{
			return Convert.ToSingle(Double() * (double)(max - min) + (double)min);
		}

		public byte Byte(byte min = 0, byte max = byte.MaxValue)
		{
			return Convert.ToByte(Number(min, max));
		}

		public byte[] Bytes(int count)
		{
			byte[] array = new byte[count];
			lock (Locker.Value)
			{
				localSeed.NextBytes(array);
				return array;
			}
		}

		public sbyte SByte(sbyte min = sbyte.MinValue, sbyte max = sbyte.MaxValue)
		{
			return Convert.ToSByte(Number(min, max));
		}

		public int Int(int min = int.MinValue, int max = int.MaxValue)
		{
			return Number(min, max);
		}

		public uint UInt(uint min = 0u, uint max = uint.MaxValue)
		{
			return Convert.ToUInt32(Double() * (double)(max - min) + (double)min);
		}

		public ulong ULong(ulong min = 0uL, ulong max = ulong.MaxValue)
		{
			return Convert.ToUInt64(Double() * (double)(max - min) + (double)min);
		}

		public long Long(long min = long.MinValue, long max = long.MaxValue)
		{
			decimal num = (decimal)max - (decimal)min;
			return Convert.ToInt64((decimal)Double() * num + (decimal)min);
		}

		public short Short(short min = short.MinValue, short max = short.MaxValue)
		{
			return Convert.ToInt16(Double() * (double)(max - min) + (double)min);
		}

		public ushort UShort(ushort min = 0, ushort max = ushort.MaxValue)
		{
			return Convert.ToUInt16(Double() * (double)(max - min) + (double)(int)min);
		}

		public char Char(char min = '\0', char max = '\uffff')
		{
			return Convert.ToChar(Number(min, max));
		}

		public char[] Chars(char min = '\0', char max = '\uffff', int count = 5)
		{
			char[] array = new char[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = Char(min, max);
			}
			return array;
		}

		public string String(int? length = null, char minChar = '\0', char maxChar = '\uffff')
		{
			int count = length ?? Number(40, 80);
			return new string(Chars(minChar, maxChar, count));
		}

		public string String(int minLength, int maxLength, char minChar = '\0', char maxChar = '\uffff')
		{
			int value = Number(minLength, maxLength);
			return String((int?)value, minChar, maxChar);
		}

		public string String2(int length, string chars = "abcdefghijklmnopqrstuvwxyz")
		{
			char[] array = new char[length];
			for (int i = 0; i < length; i++)
			{
				int index = Number(0, chars.Length - 1);
				array[i] = chars[index];
			}
			return new string(array);
		}

		public string String2(int minLength, int maxLength, string chars = "abcdefghijklmnopqrstuvwxyz")
		{
			int length = Number(minLength, maxLength);
			return String2(length, chars);
		}

		public string Utf16String(int minLength = 40, int maxLength = 80, bool excludeSurrogates = false)
		{
			int num = ((minLength == maxLength) ? minLength : Number(minLength, maxLength));
			StringBuilder stringBuilder = new StringBuilder();
			while (stringBuilder.Length < num)
			{
				int num2 = num - stringBuilder.Length;
				string text = null;
				int num3 = 0;
				if (!excludeSurrogates && num2 >= 2 && Bool())
				{
					text = ArrayElement(SafeUnicodeRanges.SurrogatePairs);
					num3 = 1;
				}
				else
				{
					text = ArrayElement(SafeUnicodeRanges.Basic);
					num3 = 0;
				}
				char min = text[num3];
				char max = text[2 + num3 * 2];
				char value = (char)UShort(min, max);
				if (num3 == 1)
				{
					stringBuilder.Append(text[0]);
					stringBuilder.Append(value);
				}
				else
				{
					stringBuilder.Append(value);
				}
			}
			return stringBuilder.ToString();
		}

		public string Hash(int length = 40, bool upperCase = false)
		{
			return String2(length, upperCase ? "0123456789ABCDEF" : "0123456789abcdef");
		}

		public bool Bool()
		{
			return Number() == 0;
		}

		public bool Bool(float weight)
		{
			return Float() < weight;
		}

		public T ArrayElement<T>(T[] array)
		{
			if (array.Length == 0)
			{
				throw new ArgumentException("The array is empty. There are no items to select.", "array");
			}
			int num = Number(array.Length - 1);
			return array[num];
		}

		public BValue ArrayElement(BArray props, int? min = null, int? max = null)
		{
			int index = Number(min.GetValueOrDefault(), (max - 1) ?? (props.Count - 1));
			return props[index];
		}

		public string ArrayElement(Array array)
		{
			if (array == null)
			{
				array = new string[3] { "a", "b", "c" };
			}
			int index = Number(array.Length - 1);
			return array.GetValue(index).ToString();
		}

		public T[] ArrayElements<T>(T[] array, int? count = null)
		{
			if (count > array.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (!count.HasValue)
			{
				count = Number(0, array.Length - 1);
			}
			return Shuffle(array).Take(count.Value).ToArray();
		}

		public T ListItem<T>(List<T> list)
		{
			return ListItem((IList<T>)list);
		}

		public T ListItem<T>(IList<T> list)
		{
			if (list.Count <= 0)
			{
				throw new ArgumentException("The list is empty. There are no items to select.", "list");
			}
			int index = Number(list.Count - 1);
			return list[index];
		}

		public List<T> ListItems<T>(IList<T> items, int? count = null)
		{
			if (count > items.Count)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (!count.HasValue)
			{
				count = Number(0, items.Count - 1);
			}
			return Shuffle(items).Take(count.Value).ToList();
		}

		public IList<T> ListItems<T>(List<T> items, int? count = null)
		{
			return ListItems((IList<T>)items, count);
		}

		public T CollectionItem<T>(ICollection<T> collection)
		{
			if (collection.Count <= 0)
			{
				throw new ArgumentException("The collection is empty. There are no items to select.", "collection");
			}
			int count = Number(collection.Count - 1);
			return collection.Skip(count).First();
		}

		public string ReplaceNumbers(string format, char symbol = '#')
		{
			return ReplaceSymbols(format, symbol, () => Convert.ToChar(48 + Number(9)));
		}

		public string ReplaceSymbols(string format, char symbol, Func<char> func)
		{
			return new string(format.Select((char c) => (c != symbol) ? c : func()).ToArray());
		}

		public string Replace(string format)
		{
			return new string(format.Select(delegate(char c)
			{
				if (c == '*')
				{
					c = (Bool() ? '#' : '?');
				}
				return c switch
				{
					'#' => Convert.ToChar(48 + Number(9)), 
					'?' => Convert.ToChar(65 + Number(25)), 
					_ => c, 
				};
			}).ToArray());
		}

		public string ClampString(string str, int? min = null, int? max = null)
		{
			if (max.HasValue && str.Length > max)
			{
				str = str.Substring(0, max.Value).Trim();
			}
			if (min.HasValue && min > str.Length)
			{
				string text = Replace("".PadRight((min - str.Length).Value, '?'));
				return str + text;
			}
			return str;
		}

		public T Enum<T>(params T[] exclude) where T : struct, Enum
		{
			Type e = typeof(T);
			if (!e.IsEnum())
			{
				throw new ArgumentException("When calling Enum<T>() with no parameters T must be an enum.");
			}
			string[] array = System.Enum.GetNames(e);
			if (exclude.Any())
			{
				IEnumerable<string> second = exclude.Select((T ex) => System.Enum.GetName(e, ex));
				array = array.Except(second).ToArray();
			}
			if (!array.Any())
			{
				throw new ArgumentException("There are no values after exclusion to choose from.");
			}
			System.Enum.TryParse<T>(ArrayElement(array), out var result);
			return result;
		}

		public T[] EnumValues<T>(int? count = null, params T[] exclude) where T : Enum
		{
			T[] array = ((exclude.Length == 0) ? System.Enum.GetValues(typeof(T)).OfType<T>().Except(exclude)
				.ToArray() : System.Enum.GetValues(typeof(T)).OfType<T>().Except(exclude)
				.ToArray());
			if (count > array.Length || count < 0)
			{
				throw new ArgumentOutOfRangeException("count", count, $"The {"count"} parameter is {count} and the calculated set of enums has a length of {array.Length}. It is impossible to pick {count} enums from a list of {array.Length}.");
			}
			return ArrayElements(array, count);
		}

		public IEnumerable<T> Shuffle<T>(IEnumerable<T> source)
		{
			List<T> buffer = source.ToList();
			for (int i = 0; i < buffer.Count; i++)
			{
				int j;
				lock (Locker.Value)
				{
					j = localSeed.Next(i, buffer.Count);
				}
				yield return buffer[j];
				buffer[j] = buffer[i];
			}
		}

		public string Word()
		{
			if (wordFunctions == null)
			{
				wordFunctions = new WordFunctions(this);
			}
			return ListItem(wordFunctions.Functions)();
		}

		public string Words(int? count = null)
		{
			if (!count.HasValue)
			{
				count = Number(1, 3);
			}
			string[] value = WordsArray(count.Value);
			return string.Join(" ", value);
		}

		public string[] WordsArray(int min, int max)
		{
			int count = Number(min, max);
			return WordsArray(count);
		}

		public string[] WordsArray(int count)
		{
			return (from f in Enumerable.Range(1, count)
				select Word()).ToArray();
		}

		public Guid Guid()
		{
			return new Guid(Bytes(16));
		}

		public Guid Uuid()
		{
			return new Guid(Bytes(16));
		}

		public string RandomLocale()
		{
			return ArrayElement(Database.GetAllLocales());
		}

		public string AlphaNumeric(int length)
		{
			StringBuilder seed = new StringBuilder();
			return Enumerable.Range(1, length).Aggregate(seed, (StringBuilder b, int i) => b.Append(ArrayElement(AlphaChars)), (StringBuilder b) => b.ToString());
		}

		public string Hexadecimal(int length = 1, string prefix = "0x")
		{
			StringBuilder seed = new StringBuilder();
			return Enumerable.Range(1, length).Aggregate(seed, (StringBuilder b, int i) => b.Append(ArrayElement(HexChars)), (StringBuilder b) => $"{prefix}{b}");
		}

		public T WeightedRandom<T>(T[] items, float[] weights)
		{
			if (weights.Length != items.Length)
			{
				throw new ArgumentOutOfRangeException("items.Length and weights.Length must be the same.");
			}
			float num = Float();
			float num2 = 0f;
			T result = default(T);
			for (int i = 0; i < weights.Length; i++)
			{
				float num3 = num2 + weights[i];
				result = items[i];
				if (num >= num2 && num <= num3)
				{
					break;
				}
				num2 += weights[i];
			}
			return result;
		}
	}
	public class WordFunctions
	{
		public List<Func<string>> Functions { get; } = new List<Func<string>>();


		private Commerce Commerce { get; }

		private Company Company { get; }

		private Address Address { get; }

		private Finance Finance { get; }

		private Hacker Hacker { get; }

		private Name Name { get; }

		public WordFunctions(Randomizer r)
		{
			Commerce = new Commerce
			{
				Random = r
			};
			Company = new Company
			{
				Random = r
			};
			Address = new Address
			{
				Random = r
			};
			Finance = new Finance
			{
				Random = r
			};
			Hacker = new Hacker
			{
				Random = r
			};
			Name = new Name
			{
				Random = r
			};
			Init();
		}

		private void Init()
		{
			Functions.Add(() => Commerce.Department());
			Functions.Add(() => Commerce.ProductName());
			Functions.Add(() => Commerce.ProductAdjective());
			Functions.Add(() => Commerce.ProductMaterial());
			Functions.Add(() => Commerce.ProductName());
			Functions.Add(() => Commerce.Color());
			Functions.Add(() => Company.CatchPhraseAdjective());
			Functions.Add(() => Company.CatchPhraseDescriptor());
			Functions.Add(() => Company.CatchPhraseNoun());
			Functions.Add(() => Company.BsAdjective());
			Functions.Add(() => Company.BsBuzz());
			Functions.Add(() => Company.BsNoun());
			Functions.Add(() => Address.StreetSuffix());
			Functions.Add(() => Address.County());
			Functions.Add(() => Address.Country());
			Functions.Add(() => Address.State());
			Functions.Add(() => Address.StreetSuffix());
			Functions.Add(() => Finance.AccountName());
			Functions.Add(() => Finance.TransactionType());
			Functions.Add(() => Finance.Currency().Description);
			Functions.Add(() => Hacker.Noun());
			Functions.Add(() => Hacker.Verb());
			Functions.Add(() => Hacker.Adjective());
			Functions.Add(() => Hacker.IngVerb());
			Functions.Add(() => Hacker.Abbreviation());
			Functions.Add(() => Name.JobDescriptor());
			Functions.Add(() => Name.JobArea());
			Functions.Add(() => Name.JobType());
		}
	}
	public static class ResourceHelper
	{
		public static bool ResourceExists(Assembly assembly, string resourceName)
		{
			return assembly.GetManifestResourceInfo(resourceName) != null;
		}

		public static byte[] ReadResource(Assembly assembly, string resourceName)
		{
			using Stream stream = assembly.GetManifestResourceStream(resourceName);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		public static BValue ReadBValueResource(Assembly assembly, string resourceName)
		{
			using Stream stream = assembly.GetManifestResourceStream(resourceName);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return Bogus.Bson.Bson.Load(memoryStream.ToArray());
		}

		public static BObject ReadBObjectResource(Assembly assembly, string resourceName)
		{
			using Stream stream = assembly.GetManifestResourceStream(resourceName);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return Bogus.Bson.Bson.Load(memoryStream.ToArray());
		}
	}
	public class Rule<T>
	{
		public T Action { get; set; }

		public string PropertyName { get; set; }

		public string RuleSet { get; set; } = string.Empty;


		public bool ProhibitInStrictMode { get; set; }
	}
	public class PopulateAction<T> : Rule<Func<Faker, T, object>>
	{
	}
	public class FinalizeAction<T> : Rule<Action<Faker, T>>
	{
	}
	public class MultiDictionary<Key, Key2, Value> : Dictionary<Key, Dictionary<Key2, Value>>
	{
		public MultiDictionary(IEqualityComparer<Key> comparer)
			: base(comparer)
		{
		}

		public void Add(Key key, Key2 key2, Value value)
		{
			if (!TryGetValue(key, out var value2))
			{
				value2 = new Dictionary<Key2, Value>();
				Add(key, value2);
			}
			value2[key2] = value;
		}
	}
	public class MultiSetDictionary<Key, Value> : Dictionary<Key, HashSet<Value>>
	{
		public MultiSetDictionary(IEqualityComparer<Key> comparer)
			: base(comparer)
		{
		}

		public void Add(Key key, Value value)
		{
			if (!TryGetValue(key, out var value2))
			{
				value2 = new HashSet<Value>();
				Add(key, value2);
			}
			if (value2.Contains(value))
			{
				throw new ArgumentException("An item with the same key has already been added.");
			}
			value2.Add(value);
		}
	}
	public interface IHasRandomizer
	{
		Randomizer Random { set; }

		SeedNotifier GetNotifier();
	}
	public class SeedNotifier
	{
		private List<IHasRandomizer> registry = new List<IHasRandomizer>();

		public U Flow<U>(U item) where U : IHasRandomizer
		{
			registry.Add(item);
			return item;
		}

		public void Notify(Randomizer r)
		{
			foreach (IHasRandomizer item in registry)
			{
				item.Random = r;
			}
		}
	}
	public class MustashMethod
	{
		public string Name { get; set; }

		public MethodInfo Method { get; set; }

		public object[] OptionalArgs { get; set; }
	}
	public class Tokenizer
	{
		public static ILookup<string, MustashMethod> MustashMethods;

		static Tokenizer()
		{
			RegisterMustashMethods(typeof(Faker));
		}

		public static void RegisterMustashMethods(Type type)
		{
			MustashMethods = (from p in type.GetProperties()
				where p.IsDefined(typeof(RegisterMustasheMethodsAttribute), inherit: true)
				select p).SelectMany((PropertyInfo p) => (from mi in p.PropertyType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
				where mi.GetGenericArguments().Length == 0
				select mi).Select(delegate(MethodInfo mi)
			{
				string text = DataSet.ResolveCategory(p.PropertyType);
				string name = mi.Name;
				return new MustashMethod
				{
					Name = (text + "." + name).ToUpperInvariant(),
					Method = mi,
					OptionalArgs = (from pi in mi.GetParameters()
						where pi.IsOptional
						select pi into _
						select Type.Missing).ToArray()
				};
			})).ToLookup((MustashMethod mm) => mm.Name);
		}

		public static string Parse(string str, params object[] dataSets)
		{
			int num = str.IndexOf("{{", StringComparison.Ordinal);
			int num2 = str.IndexOf("}}", StringComparison.Ordinal);
			if (num == -1 && num2 == -1)
			{
				return str;
			}
			ParseMustashText(str, num, num2, out var methodName, out var arguments);
			if (!MustashMethods.Contains(methodName))
			{
				throw new ArgumentException("Unknown method " + methodName + " can't be found.");
			}
			object obj = FindDataSetWithMethod(dataSets, methodName);
			MustashMethod mustashMethod = FindMustashMethod(methodName, arguments);
			object[] array = ConvertStringArgumentsToObjects(arguments, mustashMethod);
			IEnumerable<object> second = mustashMethod.OptionalArgs.Take(mustashMethod.Method.GetParameters().Length - array.Length);
			object[] parameters = array.Concat(second).ToArray();
			string value = mustashMethod.Method.Invoke(obj, parameters).ToString();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(str, 0, num);
			stringBuilder.Append(value);
			stringBuilder.Append(str.Substring(num2 + 2));
			return Parse(stringBuilder.ToString(), dataSets);
		}

		private static object FindDataSetWithMethod(object[] dataSets, string methodName)
		{
			Type dataSetType = MustashMethods[methodName].First().Method.DeclaringType;
			return dataSets.FirstOrDefault((object o) => o.GetType() == dataSetType) ?? throw new ArgumentException($"Can't parse {methodName} because the dataset was not provided in the {"dataSets"} parameter.", "dataSets");
		}

		private static void ParseMustashText(string str, int start, int end, out string methodName, out string[] arguments)
		{
			string text = str.Substring(start + 2, end - start - 2).Replace("}}", "").Replace("{{", "");
			int num = text.IndexOf("(", StringComparison.Ordinal);
			if (num != -1)
			{
				string argumentsString = GetArgumentsString(text, num);
				methodName = text.Substring(0, num).Trim();
				arguments = (from s in argumentsString.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
					select s.Trim()).ToArray();
			}
			else
			{
				methodName = text;
				arguments = new string[0];
			}
			methodName = methodName.ToUpperInvariant();
		}

		private static MustashMethod FindMustashMethod(string methodName, string[] arguments)
		{
			return (from mm in MustashMethods[methodName]
				orderby mm.Method.GetParameters().Count((ParameterInfo pi) => pi.IsOptional) - arguments.Length
				where mm.Method.GetParameters().Length >= arguments.Length
				where mm.OptionalArgs.Length + arguments.Length >= mm.Method.GetParameters().Length
				select mm).FirstOrDefault() ?? throw new ArgumentException($"Cannot find a method '{methodName}' that could accept {arguments.Length} arguments");
		}

		private static object[] ConvertStringArgumentsToObjects(string[] parameters, MustashMethod mm)
		{
			try
			{
				return mm.Method.GetParameters().Zip(parameters, GetValueForParameter).ToArray();
			}
			catch (OverflowException innerException)
			{
				throw new ArgumentOutOfRangeException("One of the arguments for " + mm.Name + " is out of supported range. Argument list: " + string.Join(",", parameters), innerException);
			}
			catch (Exception ex) when (ex is InvalidCastException || ex is FormatException)
			{
				throw new ArgumentException("One of the arguments for " + mm.Name + " cannot be converted to target type. Argument list: " + string.Join(",", parameters), ex);
			}
			catch (Exception innerException2)
			{
				throw new ArgumentException("Cannot parse arguments for " + mm.Name + ". Argument list: " + string.Join(",", parameters), innerException2);
			}
		}

		private static object GetValueForParameter(ParameterInfo parameterInfo, string parameterValue)
		{
			Type type = Nullable.GetUnderlyingType(parameterInfo.ParameterType) ?? parameterInfo.ParameterType;
			if (typeof(Enum).IsAssignableFrom(type))
			{
				return Enum.Parse(type, parameterValue);
			}
			if (typeof(TimeSpan) == type)
			{
				return TimeSpan.Parse(parameterValue);
			}
			return Convert.ChangeType(parameterValue, type);
		}

		private static string GetArgumentsString(string methodCall, int parametersStart)
		{
			int num = methodCall.IndexOf(')');
			if (num == -1)
			{
				throw new ArgumentException("The method call '" + methodCall + "' is missing a terminating ')' character.");
			}
			return methodCall.Substring(parametersStart + 1, num - parametersStart - 1);
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	internal class RegisterMustasheMethodsAttribute : Attribute
	{
	}
	public static class Transliterater
	{
		public static Trie CharMap = BuildCharMap(new Trie());

		public static Trie DiatricMap = BuildDiatricMap(new Trie());

		public static MultiDictionary<string, string, string> LangCharMap = BuildLangCharMap(new MultiDictionary<string, string, string>(StringComparer.Ordinal));

		public static MultiDictionary<string, string, string> SymbolMap = BuildSymbolMap(new MultiDictionary<string, string, string>(StringComparer.Ordinal));

		public static readonly Dictionary<string, string> EmptyDictionary = new Dictionary<string, string>();

		public static string Translate(string input, string lang = "en")
		{
			if (!LangCharMap.TryGetValue(lang, out var value))
			{
				value = EmptyDictionary;
			}
			if (!SymbolMap.TryGetValue(lang, out var value2))
			{
				value2 = SymbolMap["en"];
			}
			StringBuilder stringBuilder = new StringBuilder(input.Length);
			for (int i = 0; i < input.Length; i++)
			{
				string text = input.Substring(i, 1);
				if (value.TryGetValue(text, out var value3))
				{
					stringBuilder.Append(value3);
					continue;
				}
				int used = 0;
				string text2 = WalkTrie(i, input, CharMap, ref used);
				if (text2 != null)
				{
					stringBuilder.Append(text2);
					i += used - 1;
					continue;
				}
				used = 0;
				string text3 = WalkTrie(i, input, DiatricMap, ref used);
				string value4;
				if (text3 != null)
				{
					stringBuilder.Append(text3);
					i += used - 1;
				}
				else if (value2.TryGetValue(text, out value4))
				{
					stringBuilder.Append(value4);
				}
				else
				{
					stringBuilder.Append(text);
				}
			}
			return stringBuilder.ToString();
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public static string WalkTrie(int i, string input, Trie trie, ref int used)
		{
			if (i >= input.Length)
			{
				return trie.Value;
			}
			string key = input.Substring(i, 1);
			if (trie.Map.TryGetValue(key, out var value))
			{
				used++;
				return WalkTrie(i + 1, input, value, ref used);
			}
			string value2 = trie.Value;
			if (value2 != null && value2.Length > 0)
			{
				return trie.Value;
			}
			return null;
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		public static Trie BuildCharMap(Trie trie)
		{
			Trie.Insert(trie, "À", "A");
			Trie.Insert(trie, "Á", "A");
			Trie.Insert(trie, "Â", "A");
			Trie.Insert(trie, "Ã", "A");
			Trie.Insert(trie, "Ä", "Ae");
			Trie.Insert(trie, "Å", "A");
			Trie.Insert(trie, "Æ", "AE");
			Trie.Insert(trie, "Ç", "C");
			Trie.Insert(trie, "È", "E");
			Trie.Insert(trie, "É", "E");
			Trie.Insert(trie, "Ê", "E");
			Trie.Insert(trie, "Ë", "E");
			Trie.Insert(trie, "Ì", "I");
			Trie.Insert(trie, "Í", "I");
			Trie.Insert(trie, "Î", "I");
			Trie.Insert(trie, "Ï", "I");
			Trie.Insert(trie, "Ð", "D");
			Trie.Insert(trie, "Ñ", "N");
			Trie.Insert(trie, "Ò", "O");
			Trie.Insert(trie, "Ó", "O");
			Trie.Insert(trie, "Ô", "O");
			Trie.Insert(trie, "Õ", "O");
			Trie.Insert(trie, "Ö", "Oe");
			Trie.Insert(trie, "Ő", "O");
			Trie.Insert(trie, "Ø", "O");
			Trie.Insert(trie, "Ù", "U");
			Trie.Insert(trie, "Ú", "U");
			Trie.Insert(trie, "Û", "U");
			Trie.Insert(trie, "Ü", "Ue");
			Trie.Insert(trie, "Ű", "U");
			Trie.Insert(trie, "Ý", "Y");
			Trie.Insert(trie, "Þ", "TH");
			Trie.Insert(trie, "ß", "ss");
			Trie.Insert(trie, "à", "a");
			Trie.Insert(trie, "á", "a");
			Trie.Insert(trie, "â", "a");
			Trie.Insert(trie, "ã", "a");
			Trie.Insert(trie, "ä", "ae");
			Trie.Insert(trie, "å", "a");
			Trie.Insert(trie, "æ", "ae");
			Trie.Insert(trie, "ç", "c");
			Trie.Insert(trie, "è", "e");
			Trie.Insert(trie, "é", "e");
			Trie.Insert(trie, "ê", "e");
			Trie.Insert(trie, "ë", "e");
			Trie.Insert(trie, "ì", "i");
			Trie.Insert(trie, "í", "i");
			Trie.Insert(trie, "î", "i");
			Trie.Insert(trie, "ï", "i");
			Trie.Insert(trie, "ð", "d");
			Trie.Insert(trie, "ñ", "n");
			Trie.Insert(trie, "ò", "o");
			Trie.Insert(trie, "ó", "o");
			Trie.Insert(trie, "ô", "o");
			Trie.Insert(trie, "õ", "o");
			Trie.Insert(trie, "ö", "oe");
			Trie.Insert(trie, "ő", "o");
			Trie.Insert(trie, "ø", "o");
			Trie.Insert(trie, "ù", "u");
			Trie.Insert(trie, "ú", "u");
			Trie.Insert(trie, "û", "u");
			Trie.Insert(trie, "ü", "ue");
			Trie.Insert(trie, "ű", "u");
			Trie.Insert(trie, "ý", "y");
			Trie.Insert(trie, "þ", "th");
			Trie.Insert(trie, "ÿ", "y");
			Trie.Insert(trie, "ẞ", "SS");
			Trie.Insert(trie, "ا", "a");
			Trie.Insert(trie, "أ", "a");
			Trie.Insert(trie, "إ", "i");
			Trie.Insert(trie, "آ", "aa");
			Trie.Insert(trie, "ؤ", "u");
			Trie.Insert(trie, "ئ", "e");
			Trie.Insert(trie, "ء", "a");
			Trie.Insert(trie, "ب", "b");
			Trie.Insert(trie, "ت", "t");
			Trie.Insert(trie, "ث", "th");
			Trie.Insert(trie, "ج", "j");
			Trie.Insert(trie, "ح", "h");
			Trie.Insert(trie, "خ", "kh");
			Trie.Insert(trie, "د", "d");
			Trie.Insert(trie, "ذ", "th");
			Trie.Insert(trie, "ر", "r");
			Trie.Insert(trie, "ز", "z");
			Trie.Insert(trie, "س", "s");
			Trie.Insert(trie, "ش", "sh");
			Trie.Insert(trie, "ص", "s");
			Trie.Insert(trie, "ض", "dh");
			Trie.Insert(trie, "ط", "t");
			Trie.Insert(trie, "ظ", "z");
			Trie.Insert(trie, "ع", "a");
			Trie.Insert(trie, "غ", "gh");
			Trie.Insert(trie, "ف", "f");
			Trie.Insert(trie, "ق", "q");
			Trie.Insert(trie, "ك", "k");
			Trie.Insert(trie, "ل", "l");
			Trie.Insert(trie, "م", "m");
			Trie.Insert(trie, "ن", "n");
			Trie.Insert(trie, "ه", "h");
			Trie.Insert(trie, "و", "w");
			Trie.Insert(trie, "ي", "y");
			Trie.Insert(trie, "ى", "a");
			Trie.Insert(trie, "ة", "h");
			Trie.Insert(trie, "ﻻ", "la");
			Trie.Insert(trie, "ﻷ", "laa");
			Trie.Insert(trie, "ﻹ", "lai");
			Trie.Insert(trie, "ﻵ", "laa");
			Trie.Insert(trie, "گ", "g");
			Trie.Insert(trie, "چ", "ch");
			Trie.Insert(trie, "پ", "p");
			Trie.Insert(trie, "ژ", "zh");
			Trie.Insert(trie, "ک", "k");
			Trie.Insert(trie, "ی", "y");
			Trie.Insert(trie, "\u064e", "a");
			Trie.Insert(trie, "\u064b", "an");
			Trie.Insert(trie, "\u0650", "e");
			Trie.Insert(trie, "\u064d", "en");
			Trie.Insert(trie, "\u064f", "u");
			Trie.Insert(trie, "\u064c", "on");
			Trie.Insert(trie, "\u0652", "");
			Trie.Insert(trie, "٠", "0");
			Trie.Insert(trie, "١", "1");
			Trie.Insert(trie, "٢", "2");
			Trie.Insert(trie, "٣", "3");
			Trie.Insert(trie, "٤", "4");
			Trie.Insert(trie, "٥", "5");
			Trie.Insert(trie, "٦", "6");
			Trie.Insert(trie, "٧", "7");
			Trie.Insert(trie, "٨", "8");
			Trie.Insert(trie, "٩", "9");
			Trie.Insert(trie, "۰", "0");
			Trie.Insert(trie, "۱", "1");
			Trie.Insert(trie, "۲", "2");
			Trie.Insert(trie, "۳", "3");
			Trie.Insert(trie, "۴", "4");
			Trie.Insert(trie, "۵", "5");
			Trie.Insert(trie, "۶", "6");
			Trie.Insert(trie, "۷", "7");
			Trie.Insert(trie, "۸", "8");
			Trie.Insert(trie, "۹", "9");
			Trie.Insert(trie, "က", "k");
			Trie.Insert(trie, "ခ", "kh");
			Trie.Insert(trie, "ဂ", "g");
			Trie.Insert(trie, "ဃ", "ga");
			Trie.Insert(trie, "င", "ng");
			Trie.Insert(trie, "စ", "s");
			Trie.Insert(trie, "ဆ", "sa");
			Trie.Insert(trie, "ဇ", "z");
			Trie.Insert(trie, "စ\u103b", "za");
			Trie.Insert(trie, "ည", "ny");
			Trie.Insert(trie, "ဋ", "t");
			Trie.Insert(trie, "ဌ", "ta");
			Trie.Insert(trie, "ဍ", "d");
			Trie.Insert(trie, "ဎ", "da");
			Trie.Insert(trie, "ဏ", "na");
			Trie.Insert(trie, "တ", "t");
			Trie.Insert(trie, "ထ", "ta");
			Trie.Insert(trie, "ဒ", "d");
			Trie.Insert(trie, "ဓ", "da");
			Trie.Insert(trie, "န", "n");
			Trie.Insert(trie, "ပ", "p");
			Trie.Insert(trie, "ဖ", "pa");
			Trie.Insert(trie, "ဗ", "b");
			Trie.Insert(trie, "ဘ", "ba");
			Trie.Insert(trie, "မ", "m");
			Trie.Insert(trie, "ယ", "y");
			Trie.Insert(trie, "ရ", "ya");
			Trie.Insert(trie, "လ", "l");
			Trie.Insert(trie, "ဝ", "w");
			Trie.Insert(trie, "သ", "th");
			Trie.Insert(trie, "ဟ", "h");
			Trie.Insert(trie, "ဠ", "la");
			Trie.Insert(trie, "အ", "a");
			Trie.Insert(trie, "\u103c", "y");
			Trie.Insert(trie, "\u103b", "ya");
			Trie.Insert(trie, "\u103d", "w");
			Trie.Insert(trie, "\u103c\u103d", "yw");
			Trie.Insert(trie, "\u103b\u103d", "ywa");
			Trie.Insert(trie, "\u103e", "h");
			Trie.Insert(trie, "ဧ", "e");
			Trie.Insert(trie, "၏", "-e");
			Trie.Insert(trie, "ဣ", "i");
			Trie.Insert(trie, "ဤ", "-i");
			Trie.Insert(trie, "ဉ", "u");
			Trie.Insert(trie, "ဦ", "-u");
			Trie.Insert(trie, "ဩ", "aw");
			Trie.Insert(trie, "သ\u103c\u1031\u102c", "aw");
			Trie.Insert(trie, "ဪ", "aw");
			Trie.Insert(trie, "၀", "0");
			Trie.Insert(trie, "၁", "1");
			Trie.Insert(trie, "၂", "2");
			Trie.Insert(trie, "၃", "3");
			Trie.Insert(trie, "၄", "4");
			Trie.Insert(trie, "၅", "5");
			Trie.Insert(trie, "၆", "6");
			Trie.Insert(trie, "၇", "7");
			Trie.Insert(trie, "၈", "8");
			Trie.Insert(trie, "၉", "9");
			Trie.Insert(trie, "\u1039", "");
			Trie.Insert(trie, "\u1037", "");
			Trie.Insert(trie, "\u1038", "");
			Trie.Insert(trie, "č", "c");
			Trie.Insert(trie, "ď", "d");
			Trie.Insert(trie, "ě", "e");
			Trie.Insert(trie, "ň", "n");
			Trie.Insert(trie, "ř", "r");
			Trie.Insert(trie, "š", "s");
			Trie.Insert(trie, "ť", "t");
			Trie.Insert(trie, "ů", "u");
			Trie.Insert(trie, "ž", "z");
			Trie.Insert(trie, "Č", "C");
			Trie.Insert(trie, "Ď", "D");
			Trie.Insert(trie, "Ě", "E");
			Trie.Insert(trie, "Ň", "N");
			Trie.Insert(trie, "Ř", "R");
			Trie.Insert(trie, "Š", "S");
			Trie.Insert(trie, "Ť", "T");
			Trie.Insert(trie, "Ů", "U");
			Trie.Insert(trie, "Ž", "Z");
			Trie.Insert(trie, "ހ", "h");
			Trie.Insert(trie, "ށ", "sh");
			Trie.Insert(trie, "ނ", "n");
			Trie.Insert(trie, "ރ", "r");
			Trie.Insert(trie, "ބ", "b");
			Trie.Insert(trie, "ޅ", "lh");
			Trie.Insert(trie, "ކ", "k");
			Trie.Insert(trie, "އ", "a");
			Trie.Insert(trie, "ވ", "v");
			Trie.Insert(trie, "މ", "m");
			Trie.Insert(trie, "ފ", "f");
			Trie.Insert(trie, "ދ", "dh");
			Trie.Insert(trie, "ތ", "th");
			Trie.Insert(trie, "ލ", "l");
			Trie.Insert(trie, "ގ", "g");
			Trie.Insert(trie, "ޏ", "gn");
			Trie.Insert(trie, "ސ", "s");
			Trie.Insert(trie, "ޑ", "d");
			Trie.Insert(trie, "ޒ", "z");
			Trie.Insert(trie, "ޓ", "t");
			Trie.Insert(trie, "ޔ", "y");
			Trie.Insert(trie, "ޕ", "p");
			Trie.Insert(trie, "ޖ", "j");
			Trie.Insert(trie, "ޗ", "ch");
			Trie.Insert(trie, "ޘ", "tt");
			Trie.Insert(trie, "ޙ", "hh");
			Trie.Insert(trie, "ޚ", "kh");
			Trie.Insert(trie, "ޛ", "th");
			Trie.Insert(trie, "ޜ", "z");
			Trie.Insert(trie, "ޝ", "sh");
			Trie.Insert(trie, "ޞ", "s");
			Trie.Insert(trie, "ޟ", "d");
			Trie.Insert(trie, "ޠ", "t");
			Trie.Insert(trie, "ޡ", "z");
			Trie.Insert(trie, "ޢ", "a");
			Trie.Insert(trie, "ޣ", "gh");
			Trie.Insert(trie, "ޤ", "q");
			Trie.Insert(trie, "ޥ", "w");
			Trie.Insert(trie, "\u07a6", "a");
			Trie.Insert(trie, "\u07a7", "aa");
			Trie.Insert(trie, "\u07a8", "i");
			Trie.Insert(trie, "\u07a9", "ee");
			Trie.Insert(trie, "\u07aa", "u");
			Trie.Insert(trie, "\u07ab", "oo");
			Trie.Insert(trie, "\u07ac", "e");
			Trie.Insert(trie, "\u07ad", "ey");
			Trie.Insert(trie, "\u07ae", "o");
			Trie.Insert(trie, "\u07af", "oa");
			Trie.Insert(trie, "\u07b0", "");
			Trie.Insert(trie, "ა", "a");
			Trie.Insert(trie, "ბ", "b");
			Trie.Insert(trie, "გ", "g");
			Trie.Insert(trie, "დ", "d");
			Trie.Insert(trie, "ე", "e");
			Trie.Insert(trie, "ვ", "v");
			Trie.Insert(trie, "ზ", "z");
			Trie.Insert(trie, "თ", "t");
			Trie.Insert(trie, "ი", "i");
			Trie.Insert(trie, "კ", "k");
			Trie.Insert(trie, "ლ", "l");
			Trie.Insert(trie, "მ", "m");
			Trie.Insert(trie, "ნ", "n");
			Trie.Insert(trie, "ო", "o");
			Trie.Insert(trie, "პ", "p");
			Trie.Insert(trie, "ჟ", "zh");
			Trie.Insert(trie, "რ", "r");
			Trie.Insert(trie, "ს", "s");
			Trie.Insert(trie, "ტ", "t");
			Trie.Insert(trie, "უ", "u");
			Trie.Insert(trie, "ფ", "p");
			Trie.Insert(trie, "ქ", "k");
			Trie.Insert(trie, "ღ", "gh");
			Trie.Insert(trie, "ყ", "q");
			Trie.Insert(trie, "შ", "sh");
			Trie.Insert(trie, "ჩ", "ch");
			Trie.Insert(trie, "ც", "ts");
			Trie.Insert(trie, "ძ", "dz");
			Trie.Insert(trie, "წ", "ts");
			Trie.Insert(trie, "ჭ", "ch");
			Trie.Insert(trie, "ხ", "kh");
			Trie.Insert(trie, "ჯ", "j");
			Trie.Insert(trie, "ჰ", "h");
			Trie.Insert(trie, "α", "a");
			Trie.Insert(trie, "β", "v");
			Trie.Insert(trie, "γ", "g");
			Trie.Insert(trie, "δ", "d");
			Trie.Insert(trie, "ε", "e");
			Trie.Insert(trie, "ζ", "z");
			Trie.Insert(trie, "η", "i");
			Trie.Insert(trie, "θ", "th");
			Trie.Insert(trie, "ι", "i");
			Trie.Insert(trie, "κ", "k");
			Trie.Insert(trie, "λ", "l");
			Trie.Insert(trie, "μ", "m");
			Trie.Insert(trie, "ν", "n");
			Trie.Insert(trie, "ξ", "ks");
			Trie.Insert(trie, "ο", "o");
			Trie.Insert(trie, "π", "p");
			Trie.Insert(trie, "ρ", "r");
			Trie.Insert(trie, "σ", "s");
			Trie.Insert(trie, "τ", "t");
			Trie.Insert(trie, "υ", "y");
			Trie.Insert(trie, "φ", "f");
			Trie.Insert(trie, "χ", "x");
			Trie.Insert(trie, "ψ", "ps");
			Trie.Insert(trie, "ω", "o");
			Trie.Insert(trie, "ά", "a");
			Trie.Insert(trie, "έ", "e");
			Trie.Insert(trie, "ί", "i");
			Trie.Insert(trie, "ό", "o");
			Trie.Insert(trie, "ύ", "y");
			Trie.Insert(trie, "ή", "i");
			Trie.Insert(trie, "ώ", "o");
			Trie.Insert(trie, "ς", "s");
			Trie.Insert(trie, "ϊ", "i");
			Trie.Insert(trie, "ΰ", "y");
			Trie.Insert(trie, "ϋ", "y");
			Trie.Insert(trie, "ΐ", "i");
			Trie.Insert(trie, "Α", "A");
			Trie.Insert(trie, "Β", "B");
			Trie.Insert(trie, "Γ", "G");
			Trie.Insert(trie, "Δ", "D");
			Trie.Insert(trie, "Ε", "E");
			Trie.Insert(trie, "Ζ", "Z");
			Trie.Insert(trie, "Η", "I");
			Trie.Insert(trie, "Θ", "TH");
			Trie.Insert(trie, "Ι", "I");
			Trie.Insert(trie, "Κ", "K");
			Trie.Insert(trie, "Λ", "L");
			Trie.Insert(trie, "Μ", "M");
			Trie.Insert(trie, "Ν", "N");
			Trie.Insert(trie, "Ξ", "KS");
			Trie.Insert(trie, "Ο", "O");
			Trie.Insert(trie, "Π", "P");
			Trie.Insert(trie, "Ρ", "R");
			Trie.Insert(trie, "Σ", "S");
			Trie.Insert(trie, "Τ", "T");
			Trie.Insert(trie, "Υ", "Y");
			Trie.Insert(trie, "Φ", "F");
			Trie.Insert(trie, "Χ", "X");
			Trie.Insert(trie, "Ψ", "PS");
			Trie.Insert(trie, "Ω", "O");
			Trie.Insert(trie, "Ά", "A");
			Trie.Insert(trie, "Έ", "E");
			Trie.Insert(trie, "Ί", "I");
			Trie.Insert(trie, "Ό", "O");
			Trie.Insert(trie, "Ύ", "Y");
			Trie.Insert(trie, "Ή", "I");
			Trie.Insert(trie, "Ώ", "O");
			Trie.Insert(trie, "Ϊ", "I");
			Trie.Insert(trie, "Ϋ", "Y");
			Trie.Insert(trie, "ā", "a");
			Trie.Insert(trie, "ē", "e");
			Trie.Insert(trie, "ģ", "g");
			Trie.Insert(trie, "ī", "i");
			Trie.Insert(trie, "ķ", "k");
			Trie.Insert(trie, "ļ", "l");
			Trie.Insert(trie, "ņ", "n");
			Trie.Insert(trie, "ū", "u");
			Trie.Insert(trie, "Ā", "A");
			Trie.Insert(trie, "Ē", "E");
			Trie.Insert(trie, "Ģ", "G");
			Trie.Insert(trie, "Ī", "I");
			Trie.Insert(trie, "Ķ", "k");
			Trie.Insert(trie, "Ļ", "L");
			Trie.Insert(trie, "Ņ", "N");
			Trie.Insert(trie, "Ū", "U");
			Trie.Insert(trie, "Ќ", "Kj");
			Trie.Insert(trie, "ќ", "kj");
			Trie.Insert(trie, "Љ", "Lj");
			Trie.Insert(trie, "љ", "lj");
			Trie.Insert(trie, "Њ", "Nj");
			Trie.Insert(trie, "њ", "nj");
			Trie.Insert(trie, "Тс", "Ts");
			Trie.Insert(trie, "тс", "ts");
			Trie.Insert(trie, "ą", "a");
			Trie.Insert(trie, "ć", "c");
			Trie.Insert(trie, "ę", "e");
			Trie.Insert(trie, "ł", "l");
			Trie.Insert(trie, "ń", "n");
			Trie.Insert(trie, "ś", "s");
			Trie.Insert(trie, "ź", "z");
			Trie.Insert(trie, "ż", "z");
			Trie.Insert(trie, "Ą", "A");
			Trie.Insert(trie, "Ć", "C");
			Trie.Insert(trie, "Ę", "E");
			Trie.Insert(trie, "Ł", "L");
			Trie.Insert(trie, "Ń", "N");
			Trie.Insert(trie, "Ś", "S");
			Trie.Insert(trie, "Ź", "Z");
			Trie.Insert(trie, "Ż", "Z");
			Trie.Insert(trie, "Є", "Ye");
			Trie.Insert(trie, "І", "I");
			Trie.Insert(trie, "Ї", "Yi");
			Trie.Insert(trie, "Ґ", "G");
			Trie.Insert(trie, "є", "ye");
			Trie.Insert(trie, "і", "i");
			Trie.Insert(trie, "ї", "yi");
			Trie.Insert(trie, "ґ", "g");
			Trie.Insert(trie, "ă", "a");
			Trie.Insert(trie, "Ă", "A");
			Trie.Insert(trie, "ș", "s");
			Trie.Insert(trie, "Ș", "S");
			Trie.Insert(trie, "ț", "t");
			Trie.Insert(trie, "Ț", "T");
			Trie.Insert(trie, "ţ", "t");
			Trie.Insert(trie, "Ţ", "T");
			Trie.Insert(trie, "а", "a");
			Trie.Insert(trie, "б", "b");
			Trie.Insert(trie, "в", "v");
			Trie.Insert(trie, "г", "g");
			Trie.Insert(trie, "д", "d");
			Trie.Insert(trie, "е", "e");
			Trie.Insert(trie, "ё", "yo");
			Trie.Insert(trie, "ж", "zh");
			Trie.Insert(trie, "з", "z");
			Trie.Insert(trie, "и", "i");
			Trie.Insert(trie, "й", "i");
			Trie.Insert(trie, "к", "k");
			Trie.Insert(trie, "л", "l");
			Trie.Insert(trie, "м", "m");
			Trie.Insert(trie, "н", "n");
			Trie.Insert(trie, "о", "o");
			Trie.Insert(trie, "п", "p");
			Trie.Insert(trie, "р", "r");
			Trie.Insert(trie, "с", "s");
			Trie.Insert(trie, "т", "t");
			Trie.Insert(trie, "у", "u");
			Trie.Insert(trie, "ф", "f");
			Trie.Insert(trie, "х", "kh");
			Trie.Insert(trie, "ц", "c");
			Trie.Insert(trie, "ч", "ch");
			Trie.Insert(trie, "ш", "sh");
			Trie.Insert(trie, "щ", "sh");
			Trie.Insert(trie, "ъ", "");
			Trie.Insert(trie, "ы", "y");
			Trie.Insert(trie, "ь", "");
			Trie.Insert(trie, "э", "e");
			Trie.Insert(trie, "ю", "yu");
			Trie.Insert(trie, "я", "ya");
			Trie.Insert(trie, "А", "A");
			Trie.Insert(trie, "Б", "B");
			Trie.Insert(trie, "В", "V");
			Trie.Insert(trie, "Г", "G");
			Trie.Insert(trie, "Д", "D");
			Trie.Insert(trie, "Е", "E");
			Trie.Insert(trie, "Ё", "Yo");
			Trie.Insert(trie, "Ж", "Zh");
			Trie.Insert(trie, "З", "Z");
			Trie.Insert(trie, "И", "I");
			Trie.Insert(trie, "Й", "I");
			Trie.Insert(trie, "К", "K");
			Trie.Insert(trie, "Л", "L");
			Trie.Insert(trie, "М", "M");
			Trie.Insert(trie, "Н", "N");
			Trie.Insert(trie, "О", "O");
			Trie.Insert(trie, "П", "P");
			Trie.Insert(trie, "Р", "R");
			Trie.Insert(trie, "С", "S");
			Trie.Insert(trie, "Т", "T");
			Trie.Insert(trie, "У", "U");
			Trie.Insert(trie, "Ф", "F");
			Trie.Insert(trie, "Х", "Kh");
			Trie.Insert(trie, "Ц", "C");
			Trie.Insert(trie, "Ч", "Ch");
			Trie.Insert(trie, "Ш", "Sh");
			Trie.Insert(trie, "Щ", "Sh");
			Trie.Insert(trie, "Ъ", "");
			Trie.Insert(trie, "Ы", "Y");
			Trie.Insert(trie, "Ь", "");
			Trie.Insert(trie, "Э", "E");
			Trie.Insert(trie, "Ю", "Yu");
			Trie.Insert(trie, "Я", "Ya");
			Trie.Insert(trie, "ђ", "dj");
			Trie.Insert(trie, "ј", "j");
			Trie.Insert(trie, "ћ", "c");
			Trie.Insert(trie, "џ", "dz");
			Trie.Insert(trie, "Ђ", "Dj");
			Trie.Insert(trie, "Ј", "j");
			Trie.Insert(trie, "Ћ", "C");
			Trie.Insert(trie, "Џ", "Dz");
			Trie.Insert(trie, "ľ", "l");
			Trie.Insert(trie, "ĺ", "l");
			Trie.Insert(trie, "ŕ", "r");
			Trie.Insert(trie, "Ľ", "L");
			Trie.Insert(trie, "Ĺ", "L");
			Trie.Insert(trie, "Ŕ", "R");
			Trie.Insert(trie, "ş", "s");
			Trie.Insert(trie, "Ş", "S");
			Trie.Insert(trie, "ı", "i");
			Trie.Insert(trie, "İ", "I");
			Trie.Insert(trie, "ğ", "g");
			Trie.Insert(trie, "Ğ", "G");
			Trie.Insert(trie, "ả", "a");
			Trie.Insert(trie, "Ả", "A");
			Trie.Insert(trie, "ẳ", "a");
			Trie.Insert(trie, "Ẳ", "A");
			Trie.Insert(trie, "ẩ", "a");
			Trie.Insert(trie, "Ẩ", "A");
			Trie.Insert(trie, "đ", "d");
			Trie.Insert(trie, "Đ", "D");
			Trie.Insert(trie, "ẹ", "e");
			Trie.Insert(trie, "Ẹ", "E");
			Trie.Insert(trie, "ẽ", "e");
			Trie.Insert(trie, "Ẽ", "E");
			Trie.Insert(trie, "ẻ", "e");
			Trie.Insert(trie, "Ẻ", "E");
			Trie.Insert(trie, "ế", "e");
			Trie.Insert(trie, "Ế", "E");
			Trie.Insert(trie, "ề", "e");
			Trie.Insert(trie, "Ề", "E");
			Trie.Insert(trie, "ệ", "e");
			Trie.Insert(trie, "Ệ", "E");
			Trie.Insert(trie, "ễ", "e");
			Trie.Insert(trie, "Ễ", "E");
			Trie.Insert(trie, "ể", "e");
			Trie.Insert(trie, "Ể", "E");
			Trie.Insert(trie, "ỏ", "o");
			Trie.Insert(trie, "ọ", "o");
			Trie.Insert(trie, "Ọ", "o");
			Trie.Insert(trie, "ố", "o");
			Trie.Insert(trie, "Ố", "O");
			Trie.Insert(trie, "ồ", "o");
			Trie.Insert(trie, "Ồ", "O");
			Trie.Insert(trie, "ổ", "o");
			Trie.Insert(trie, "Ổ", "O");
			Trie.Insert(trie, "ộ", "o");
			Trie.Insert(trie, "Ộ", "O");
			Trie.Insert(trie, "ỗ", "o");
			Trie.Insert(trie, "Ỗ", "O");
			Trie.Insert(trie, "ơ", "o");
			Trie.Insert(trie, "Ơ", "O");
			Trie.Insert(trie, "ớ", "o");
			Trie.Insert(trie, "Ớ", "O");
			Trie.Insert(trie, "ờ", "o");
			Trie.Insert(trie, "Ờ", "O");
			Trie.Insert(trie, "ợ", "o");
			Trie.Insert(trie, "Ợ", "O");
			Trie.Insert(trie, "ỡ", "o");
			Trie.Insert(trie, "Ỡ", "O");
			Trie.Insert(trie, "Ở", "o");
			Trie.Insert(trie, "ở", "o");
			Trie.Insert(trie, "ị", "i");
			Trie.Insert(trie, "Ị", "I");
			Trie.Insert(trie, "ĩ", "i");
			Trie.Insert(trie, "Ĩ", "I");
			Trie.Insert(trie, "ỉ", "i");
			Trie.Insert(trie, "Ỉ", "i");
			Trie.Insert(trie, "ủ", "u");
			Trie.Insert(trie, "Ủ", "U");
			Trie.Insert(trie, "ụ", "u");
			Trie.Insert(trie, "Ụ", "U");
			Trie.Insert(trie, "ũ", "u");
			Trie.Insert(trie, "Ũ", "U");
			Trie.Insert(trie, "ư", "u");
			Trie.Insert(trie, "Ư", "U");
			Trie.Insert(trie, "ứ", "u");
			Trie.Insert(trie, "Ứ", "U");
			Trie.Insert(trie, "ừ", "u");
			Trie.Insert(trie, "Ừ", "U");
			Trie.Insert(trie, "ự", "u");
			Trie.Insert(trie, "Ự", "U");
			Trie.Insert(trie, "ữ", "u");
			Trie.Insert(trie, "Ữ", "U");
			Trie.Insert(trie, "ử", "u");
			Trie.Insert(trie, "Ử", "ư");
			Trie.Insert(trie, "ỷ", "y");
			Trie.Insert(trie, "Ỷ", "y");
			Trie.Insert(trie, "ỳ", "y");
			Trie.Insert(trie, "Ỳ", "Y");
			Trie.Insert(trie, "ỵ", "y");
			Trie.Insert(trie, "Ỵ", "Y");
			Trie.Insert(trie, "ỹ", "y");
			Trie.Insert(trie, "Ỹ", "Y");
			Trie.Insert(trie, "ạ", "a");
			Trie.Insert(trie, "Ạ", "A");
			Trie.Insert(trie, "ấ", "a");
			Trie.Insert(trie, "Ấ", "A");
			Trie.Insert(trie, "ầ", "a");
			Trie.Insert(trie, "Ầ", "A");
			Trie.Insert(trie, "ậ", "a");
			Trie.Insert(trie, "Ậ", "A");
			Trie.Insert(trie, "ẫ", "a");
			Trie.Insert(trie, "Ẫ", "A");
			Trie.Insert(trie, "ắ", "a");
			Trie.Insert(trie, "Ắ", "A");
			Trie.Insert(trie, "ằ", "a");
			Trie.Insert(trie, "Ằ", "A");
			Trie.Insert(trie, "ặ", "a");
			Trie.Insert(trie, "Ặ", "A");
			Trie.Insert(trie, "ẵ", "a");
			Trie.Insert(trie, "Ẵ", "A");
			Trie.Insert(trie, "⓪", "0");
			Trie.Insert(trie, "①", "1");
			Trie.Insert(trie, "②", "2");
			Trie.Insert(trie, "③", "3");
			Trie.Insert(trie, "④", "4");
			Trie.Insert(trie, "⑤", "5");
			Trie.Insert(trie, "⑥", "6");
			Trie.Insert(trie, "⑦", "7");
			Trie.Insert(trie, "⑧", "8");
			Trie.Insert(trie, "⑨", "9");
			Trie.Insert(trie, "⑩", "10");
			Trie.Insert(trie, "⑪", "11");
			Trie.Insert(trie, "⑫", "12");
			Trie.Insert(trie, "⑬", "13");
			Trie.Insert(trie, "⑭", "14");
			Trie.Insert(trie, "⑮", "15");
			Trie.Insert(trie, "⑯", "16");
			Trie.Insert(trie, "⑰", "17");
			Trie.Insert