using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Diagnostics.Windows.Configs;
using BenchmarkDotNet.Running;

namespace SomeBenchmarks
{
	class Program
	{
		static void Main(string[] args)
		{
			//BenchmarkRunner.Run<Increments>();
			//BenchmarkRunner.Run<SortedArray>();
			//BenchmarkRunner.Run<Inline>();
			BenchmarkRunner.Run<Strings>();
		}
	}

	[MemoryDiagnoser]
	[RPlotExporter, CsvMeasurementsExporter]
	[ShortRunJob]
	public class Strings
	{
		const char C = ' ';

		[Params(1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 30, 50, 100, 1000)]
		public int Count { get; set; }

		[Benchmark]
		public string String()
		{
			var s = string.Empty;
			for (var i = 0; i < Count; i++)
			{
				s += C;
			}
			return s;
		}

		[Benchmark]
		public string Builder()
		{
			var sb = new StringBuilder();
			for (var i = 0; i < Count; i++)
			{
				sb.Append(C);
			}
			return sb.ToString();
		}
	}

	[InliningDiagnoser(true, true), TailCallDiagnoser, DisassemblyDiagnoser]
	[ShortRunJob]
	public class Inline
	{
		public int P { get; } = 100;

		[Benchmark]
		public int A()
		{
			return ComplicatedMax(P, 10);
		}

		[Benchmark]
		public int B()
		{
			return SimpleMax(P, 10);
		}

		static int ComplicatedMax(int a, int b)
		{
			if (a < 0)
				throw new InvalidOperationException();
			if (b < 0)
				throw new InvalidOperationException();
			return a > b ? a : b;
		}

		static int SimpleMax(int a, int b)
		{
			return a > b ? a : b;
		}
	}

	[RyuJitX64Job]
	[MemoryDiagnoser, DisassemblyDiagnoser]
	public class Increments
	{
		double a, b, c, d;

		[Benchmark]
		public void ABCD()
		{
			a++;
			b++;
			c++;
			d++;
		}

		[Benchmark]
		public void A()
		{
			a++;
			a++;
			a++;
			a++;
		}
	}

	[MemoryDiagnoser, DisassemblyDiagnoser, HardwareCounters(HardwareCounter.BranchMispredictions)]
	[ShortRunJob]
	public class SortedArray
	{
		const int Limit = 1000;

		byte[] randomArray, sortedArray;

		[GlobalSetup]
		public void GlobalSetup()
		{
			randomArray = new byte[Limit];
			sortedArray = new byte[Limit];
			var random = new Random();
			for (var i = 0; i < Limit; i++)
				randomArray[i] = (byte)random.Next(byte.MinValue, byte.MaxValue);
			sortedArray = randomArray.OrderBy(x => x).ToArray();
		}

		[Benchmark]
		public int Sorted()
		{
			var sum = 0;
			foreach (var item in sortedArray)
			{
				if (item < 128)
					sum += item;
			}
			return sum;
		}

		[Benchmark]
		public int Random()
		{
			var sum = 0;
			foreach (var item in randomArray)
			{
				if (item < 128)
					sum += item;
			}
			return sum;
		}
	}
}
