using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

using static MsilTest.IlUtil;

namespace MsilTest
{
    partial class Program
    {
        static void Main()
        {
            DeclareLocal(typeof(int));
            DeclareLocal(typeof(string[]));
            DeclareLocal(typeof(int));

            var l0 = DefineLabel();
            var l1 = DefineLabel();

            var yes = DefineLabel();
            var no = DefineLabel();
            var res = DefineLabel();

            IL(Call, typeof(Console).GetMethod("ReadLine"));
            IL(Call, typeof(int).GetMethod("Parse", new[] { typeof(string) }));
            IL(Stloc_0);

            IL(Call, typeof(Console).GetMethod("ReadLine"));
            IL(Ldc_I4_1);
            IL(Newarr, typeof(char));
            IL(Dup);
            IL(Ldc_I4_0);
            IL(Ldc_I4_S, ' ');
            IL(Stelem_I2);

            IL(Call, typeof(string).GetMethod("Split", new[] { typeof(char[]) }));
            IL(Stloc_1);

            IL(Ldc_I4_0);
            IL(Stloc_2);

            MarkLabel(l0);
            IL(Ldloc_0);
            IL(Ldloc_2);
            IL(Ble, yes);

            IL(Ldloc_1);
            IL(Ldloc_2);
            IL(Ldelem_Ref);
            IL(Call, typeof(int).GetMethod("Parse", new[] { typeof(string) }));
            IL(Dup);
            IL(Ldc_I4_2);
            IL(Rem);
            IL(Brtrue, l1);
            IL(Dup);
            IL(Ldc_I4_3);
            IL(Rem);
            IL(Brfalse, l1);
            IL(Dup);
            IL(Ldc_I4_5);
            IL(Rem);
            IL(Brtrue, no);

            MarkLabel(l1);
            IL(Pop);
            IL(Ldloc_2);
            IL(Ldc_I4_1);
            IL(Add);
            IL(Stloc_2);
            IL(Br, l0);

            MarkLabel(yes);
            IL(Ldstr, "APPROVED");
            IL(Br, res);
            MarkLabel(no);
            IL(Pop);
            IL(Ldstr, "DENIED");

            MarkLabel(res);
            IL(Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));
            IL(Ret);
            Run();
#if DEBUG
            Save();
            Console.ReadKey();
#endif
        }
    }

    #region こんなクソコード、見たら倒れちゃ～う！

    public static partial class IlUtil
    {
        const string asmName = "hoge";
        private static readonly AssemblyBuilder assemblyBuilder;
        private static readonly TypeBuilder typeBuilder;
        private static readonly ILGenerator ilGenerator;

        static IlUtil()
        {
#if NETFRAMEWORK
            assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(asmName), AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(asmName, asmName + ".dll");
#elif NETCOREAPP
            assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(asmName), AssemblyBuilderAccess.Run);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(asmName);
#endif
            typeBuilder = moduleBuilder.DefineType("Fuga", TypeAttributes.Public);
            var function = typeBuilder.DefineMethod("Piyo", MethodAttributes.Public | MethodAttributes.Static, null, null);
            ilGenerator = function.GetILGenerator();
        }

        public static void Run()
        {
            var type = typeBuilder.CreateType();
            type.GetMethod("Piyo").Invoke(null, null);
        }

#if NETFRAMEWORK
        public static void Save()
        {
            typeBuilder.CreateType();
            assemblyBuilder.Save(asmName + ".dll");
        }
#endif
    }

    // Opcodeをすべてフィールド定義(OpCodes.を書きたくない)
    partial class Program
    {
        private static readonly OpCode Nop = OpCodes.Nop;
        private static readonly OpCode Ldelem_U2 = OpCodes.Ldelem_U2;
        private static readonly OpCode Ldelem_I4 = OpCodes.Ldelem_I4;
        private static readonly OpCode Ldelem_U4 = OpCodes.Ldelem_U4;
        private static readonly OpCode Ldelem_I8 = OpCodes.Ldelem_I8;
        private static readonly OpCode Ldelem_I = OpCodes.Ldelem_I;
        private static readonly OpCode Ldelem_R4 = OpCodes.Ldelem_R4;
        private static readonly OpCode Ldelem_R8 = OpCodes.Ldelem_R8;
        private static readonly OpCode Ldelem_Ref = OpCodes.Ldelem_Ref;
        private static readonly OpCode Stelem_I = OpCodes.Stelem_I;
        private static readonly OpCode Stelem_I1 = OpCodes.Stelem_I1;
        private static readonly OpCode Stelem_I2 = OpCodes.Stelem_I2;
        private static readonly OpCode Ldelem_I2 = OpCodes.Ldelem_I2;
        private static readonly OpCode Stelem_I4 = OpCodes.Stelem_I4;
        private static readonly OpCode Stelem_R4 = OpCodes.Stelem_R4;
        private static readonly OpCode Stelem_R8 = OpCodes.Stelem_R8;
        private static readonly OpCode Stelem_Ref = OpCodes.Stelem_Ref;
        private static readonly OpCode Ldelem = OpCodes.Ldelem;
        private static readonly OpCode Stelem = OpCodes.Stelem;
        private static readonly OpCode Unbox_Any = OpCodes.Unbox_Any;
        private static readonly OpCode Conv_Ovf_I1 = OpCodes.Conv_Ovf_I1;
        private static readonly OpCode Conv_Ovf_U1 = OpCodes.Conv_Ovf_U1;
        private static readonly OpCode Conv_Ovf_I2 = OpCodes.Conv_Ovf_I2;
        private static readonly OpCode Conv_Ovf_U2 = OpCodes.Conv_Ovf_U2;
        private static readonly OpCode Conv_Ovf_I4 = OpCodes.Conv_Ovf_I4;
        private static readonly OpCode Stelem_I8 = OpCodes.Stelem_I8;
        private static readonly OpCode Ldelem_U1 = OpCodes.Ldelem_U1;
        private static readonly OpCode Ldelem_I1 = OpCodes.Ldelem_I1;
        private static readonly OpCode Ldelema = OpCodes.Ldelema;
        private static readonly OpCode Castclass = OpCodes.Castclass;
        private static readonly OpCode Isinst = OpCodes.Isinst;
        private static readonly OpCode Conv_R_Un = OpCodes.Conv_R_Un;
        private static readonly OpCode Unbox = OpCodes.Unbox;
        private static readonly OpCode Throw = OpCodes.Throw;
        private static readonly OpCode Ldfld = OpCodes.Ldfld;
        private static readonly OpCode Ldflda = OpCodes.Ldflda;
        private static readonly OpCode Stfld = OpCodes.Stfld;
        private static readonly OpCode Ldsfld = OpCodes.Ldsfld;
        private static readonly OpCode Ldsflda = OpCodes.Ldsflda;
        private static readonly OpCode Stsfld = OpCodes.Stsfld;
        private static readonly OpCode Stobj = OpCodes.Stobj;
        private static readonly OpCode Conv_Ovf_I1_Un = OpCodes.Conv_Ovf_I1_Un;
        private static readonly OpCode Conv_Ovf_I2_Un = OpCodes.Conv_Ovf_I2_Un;
        private static readonly OpCode Conv_Ovf_I4_Un = OpCodes.Conv_Ovf_I4_Un;
        private static readonly OpCode Conv_Ovf_I8_Un = OpCodes.Conv_Ovf_I8_Un;
        private static readonly OpCode Conv_Ovf_U1_Un = OpCodes.Conv_Ovf_U1_Un;
        private static readonly OpCode Conv_Ovf_U2_Un = OpCodes.Conv_Ovf_U2_Un;
        private static readonly OpCode Conv_Ovf_U4_Un = OpCodes.Conv_Ovf_U4_Un;
        private static readonly OpCode Conv_Ovf_U8_Un = OpCodes.Conv_Ovf_U8_Un;
        private static readonly OpCode Conv_Ovf_I_Un = OpCodes.Conv_Ovf_I_Un;
        private static readonly OpCode Conv_Ovf_U_Un = OpCodes.Conv_Ovf_U_Un;
        private static readonly OpCode Box = OpCodes.Box;
        private static readonly OpCode Newarr = OpCodes.Newarr;
        private static readonly OpCode Ldlen = OpCodes.Ldlen;
        private static readonly OpCode Conv_Ovf_U4 = OpCodes.Conv_Ovf_U4;
        private static readonly OpCode Newobj = OpCodes.Newobj;
        private static readonly OpCode Conv_Ovf_I8 = OpCodes.Conv_Ovf_I8;
        private static readonly OpCode Refanyval = OpCodes.Refanyval;
        private static readonly OpCode Cgt = OpCodes.Cgt;
        private static readonly OpCode Cgt_Un = OpCodes.Cgt_Un;
        private static readonly OpCode Clt = OpCodes.Clt;
        private static readonly OpCode Clt_Un = OpCodes.Clt_Un;
        private static readonly OpCode Ldftn = OpCodes.Ldftn;
        private static readonly OpCode Ldvirtftn = OpCodes.Ldvirtftn;
        private static readonly OpCode Ldarg = OpCodes.Ldarg;
        private static readonly OpCode Ldarga = OpCodes.Ldarga;
        private static readonly OpCode Starg = OpCodes.Starg;
        private static readonly OpCode Ldloc = OpCodes.Ldloc;
        private static readonly OpCode Ldloca = OpCodes.Ldloca;
        private static readonly OpCode Ceq = OpCodes.Ceq;
        private static readonly OpCode Stloc = OpCodes.Stloc;
        private static readonly OpCode Endfilter = OpCodes.Endfilter;
        private static readonly OpCode Unaligned = OpCodes.Unaligned;
        private static readonly OpCode Volatile = OpCodes.Volatile;
        private static readonly OpCode Tailcall = OpCodes.Tailcall;
        private static readonly OpCode Initobj = OpCodes.Initobj;
        private static readonly OpCode Constrained = OpCodes.Constrained;
        private static readonly OpCode Cpblk = OpCodes.Cpblk;
        private static readonly OpCode Initblk = OpCodes.Initblk;
        private static readonly OpCode Rethrow = OpCodes.Rethrow;
        private static readonly OpCode Sizeof = OpCodes.Sizeof;
        private static readonly OpCode Refanytype = OpCodes.Refanytype;
        private static readonly OpCode Localloc = OpCodes.Localloc;
        private static readonly OpCode Arglist = OpCodes.Arglist;
        private static readonly OpCode Prefixref = OpCodes.Prefixref;
        private static readonly OpCode Prefix1 = OpCodes.Prefix1;
        private static readonly OpCode Ckfinite = OpCodes.Ckfinite;
        private static readonly OpCode Mkrefany = OpCodes.Mkrefany;
        private static readonly OpCode Ldtoken = OpCodes.Ldtoken;
        private static readonly OpCode Conv_U2 = OpCodes.Conv_U2;
        private static readonly OpCode Conv_U1 = OpCodes.Conv_U1;
        private static readonly OpCode Conv_I = OpCodes.Conv_I;
        private static readonly OpCode Conv_Ovf_I = OpCodes.Conv_Ovf_I;
        private static readonly OpCode Conv_Ovf_U = OpCodes.Conv_Ovf_U;
        private static readonly OpCode Add_Ovf = OpCodes.Add_Ovf;
        private static readonly OpCode Add_Ovf_Un = OpCodes.Add_Ovf_Un;
        private static readonly OpCode Mul_Ovf = OpCodes.Mul_Ovf;
        private static readonly OpCode Mul_Ovf_Un = OpCodes.Mul_Ovf_Un;
        private static readonly OpCode Sub_Ovf = OpCodes.Sub_Ovf;
        private static readonly OpCode Sub_Ovf_Un = OpCodes.Sub_Ovf_Un;
        private static readonly OpCode Endfinally = OpCodes.Endfinally;
        private static readonly OpCode Leave = OpCodes.Leave;
        private static readonly OpCode Leave_S = OpCodes.Leave_S;
        private static readonly OpCode Stind_I = OpCodes.Stind_I;
        private static readonly OpCode Conv_U = OpCodes.Conv_U;
        private static readonly OpCode Prefix7 = OpCodes.Prefix7;
        private static readonly OpCode Prefix6 = OpCodes.Prefix6;
        private static readonly OpCode Prefix5 = OpCodes.Prefix5;
        private static readonly OpCode Prefix4 = OpCodes.Prefix4;
        private static readonly OpCode Prefix3 = OpCodes.Prefix3;
        private static readonly OpCode Prefix2 = OpCodes.Prefix2;
        private static readonly OpCode Conv_Ovf_U8 = OpCodes.Conv_Ovf_U8;
        private static readonly OpCode Readonly = OpCodes.Readonly;
        private static readonly OpCode Ldstr = OpCodes.Ldstr;
        private static readonly OpCode Cpobj = OpCodes.Cpobj;
        private static readonly OpCode Ldc_I4_8 = OpCodes.Ldc_I4_8;
        private static readonly OpCode Ldc_I4_S = OpCodes.Ldc_I4_S;
        private static readonly OpCode Ldc_I4 = OpCodes.Ldc_I4;
        private static readonly OpCode Ldc_I8 = OpCodes.Ldc_I8;
        private static readonly OpCode Ldc_R4 = OpCodes.Ldc_R4;
        private static readonly OpCode Ldc_R8 = OpCodes.Ldc_R8;
        private static readonly OpCode Dup = OpCodes.Dup;
        private static readonly OpCode Pop = OpCodes.Pop;
        private static readonly OpCode Jmp = OpCodes.Jmp;
        private static readonly OpCode Call = OpCodes.Call;
        private static readonly OpCode Calli = OpCodes.Calli;
        private static readonly OpCode Ldc_I4_7 = OpCodes.Ldc_I4_7;
        private static readonly OpCode Ret = OpCodes.Ret;
        private static readonly OpCode Brfalse_S = OpCodes.Brfalse_S;
        private static readonly OpCode Brtrue_S = OpCodes.Brtrue_S;
        private static readonly OpCode Beq_S = OpCodes.Beq_S;
        private static readonly OpCode Bge_S = OpCodes.Bge_S;
        private static readonly OpCode Bgt_S = OpCodes.Bgt_S;
        private static readonly OpCode Ble_S = OpCodes.Ble_S;
        private static readonly OpCode Blt_S = OpCodes.Blt_S;
        private static readonly OpCode Bne_Un_S = OpCodes.Bne_Un_S;
        private static readonly OpCode Bge_Un_S = OpCodes.Bge_Un_S;
        private static readonly OpCode Bgt_Un_S = OpCodes.Bgt_Un_S;
        private static readonly OpCode Ble_Un_S = OpCodes.Ble_Un_S;
        private static readonly OpCode Br_S = OpCodes.Br_S;
        private static readonly OpCode Ldc_I4_6 = OpCodes.Ldc_I4_6;
        private static readonly OpCode Ldc_I4_5 = OpCodes.Ldc_I4_5;
        private static readonly OpCode Ldc_I4_4 = OpCodes.Ldc_I4_4;
        private static readonly OpCode Break = OpCodes.Break;
        private static readonly OpCode Ldarg_0 = OpCodes.Ldarg_0;
        private static readonly OpCode Ldarg_1 = OpCodes.Ldarg_1;
        private static readonly OpCode Ldarg_2 = OpCodes.Ldarg_2;
        private static readonly OpCode Ldarg_3 = OpCodes.Ldarg_3;
        private static readonly OpCode Ldloc_0 = OpCodes.Ldloc_0;
        private static readonly OpCode Ldloc_1 = OpCodes.Ldloc_1;
        private static readonly OpCode Ldloc_2 = OpCodes.Ldloc_2;
        private static readonly OpCode Ldloc_3 = OpCodes.Ldloc_3;
        private static readonly OpCode Stloc_0 = OpCodes.Stloc_0;
        private static readonly OpCode Stloc_1 = OpCodes.Stloc_1;
        private static readonly OpCode Stloc_2 = OpCodes.Stloc_2;
        private static readonly OpCode Stloc_3 = OpCodes.Stloc_3;
        private static readonly OpCode Ldarg_S = OpCodes.Ldarg_S;
        private static readonly OpCode Ldarga_S = OpCodes.Ldarga_S;
        private static readonly OpCode Starg_S = OpCodes.Starg_S;
        private static readonly OpCode Ldloc_S = OpCodes.Ldloc_S;
        private static readonly OpCode Ldloca_S = OpCodes.Ldloca_S;
        private static readonly OpCode Stloc_S = OpCodes.Stloc_S;
        private static readonly OpCode Ldnull = OpCodes.Ldnull;
        private static readonly OpCode Ldc_I4_M1 = OpCodes.Ldc_I4_M1;
        private static readonly OpCode Ldc_I4_0 = OpCodes.Ldc_I4_0;
        private static readonly OpCode Ldc_I4_1 = OpCodes.Ldc_I4_1;
        private static readonly OpCode Ldc_I4_2 = OpCodes.Ldc_I4_2;
        private static readonly OpCode Ldc_I4_3 = OpCodes.Ldc_I4_3;
        private static readonly OpCode Blt_Un_S = OpCodes.Blt_Un_S;
        private static readonly OpCode Ldobj = OpCodes.Ldobj;
        private static readonly OpCode Br = OpCodes.Br;
        private static readonly OpCode Brtrue = OpCodes.Brtrue;
        private static readonly OpCode Add = OpCodes.Add;
        private static readonly OpCode Sub = OpCodes.Sub;
        private static readonly OpCode Mul = OpCodes.Mul;
        private static readonly OpCode Div = OpCodes.Div;
        private static readonly OpCode Div_Un = OpCodes.Div_Un;
        private static readonly OpCode Rem = OpCodes.Rem;
        private static readonly OpCode Rem_Un = OpCodes.Rem_Un;
        private static readonly OpCode And = OpCodes.And;
        private static readonly OpCode Or = OpCodes.Or;
        private static readonly OpCode Xor = OpCodes.Xor;
        private static readonly OpCode Shl = OpCodes.Shl;
        private static readonly OpCode Stind_R8 = OpCodes.Stind_R8;
        private static readonly OpCode Shr = OpCodes.Shr;
        private static readonly OpCode Neg = OpCodes.Neg;
        private static readonly OpCode Not = OpCodes.Not;
        private static readonly OpCode Conv_I1 = OpCodes.Conv_I1;
        private static readonly OpCode Conv_I2 = OpCodes.Conv_I2;
        private static readonly OpCode Conv_I4 = OpCodes.Conv_I4;
        private static readonly OpCode Conv_I8 = OpCodes.Conv_I8;
        private static readonly OpCode Conv_R4 = OpCodes.Conv_R4;
        private static readonly OpCode Conv_R8 = OpCodes.Conv_R8;
        private static readonly OpCode Conv_U4 = OpCodes.Conv_U4;
        private static readonly OpCode Conv_U8 = OpCodes.Conv_U8;
        private static readonly OpCode Callvirt = OpCodes.Callvirt;
        private static readonly OpCode Shr_Un = OpCodes.Shr_Un;
        private static readonly OpCode Stind_R4 = OpCodes.Stind_R4;
        private static readonly OpCode Stind_I8 = OpCodes.Stind_I8;
        private static readonly OpCode Stind_I4 = OpCodes.Stind_I4;
        private static readonly OpCode Beq = OpCodes.Beq;
        private static readonly OpCode Bge = OpCodes.Bge;
        private static readonly OpCode Bgt = OpCodes.Bgt;
        private static readonly OpCode Ble = OpCodes.Ble;
        private static readonly OpCode Blt = OpCodes.Blt;
        private static readonly OpCode Bne_Un = OpCodes.Bne_Un;
        private static readonly OpCode Bge_Un = OpCodes.Bge_Un;
        private static readonly OpCode Bgt_Un = OpCodes.Bgt_Un;
        private static readonly OpCode Ble_Un = OpCodes.Ble_Un;
        private static readonly OpCode Blt_Un = OpCodes.Blt_Un;
        private static readonly OpCode Switch = OpCodes.Switch;
        private static readonly OpCode Ldind_I1 = OpCodes.Ldind_I1;
        private static readonly OpCode Ldind_U1 = OpCodes.Ldind_U1;
        private static readonly OpCode Ldind_I2 = OpCodes.Ldind_I2;
        private static readonly OpCode Ldind_U2 = OpCodes.Ldind_U2;
        private static readonly OpCode Ldind_I4 = OpCodes.Ldind_I4;
        private static readonly OpCode Ldind_U4 = OpCodes.Ldind_U4;
        private static readonly OpCode Ldind_I8 = OpCodes.Ldind_I8;
        private static readonly OpCode Ldind_I = OpCodes.Ldind_I;
        private static readonly OpCode Ldind_R4 = OpCodes.Ldind_R4;
        private static readonly OpCode Ldind_R8 = OpCodes.Ldind_R8;
        private static readonly OpCode Ldind_Ref = OpCodes.Ldind_Ref;
        private static readonly OpCode Stind_Ref = OpCodes.Stind_Ref;
        private static readonly OpCode Stind_I1 = OpCodes.Stind_I1;
        private static readonly OpCode Stind_I2 = OpCodes.Stind_I2;
        private static readonly OpCode Brfalse = OpCodes.Brfalse;
    }

    // IlGenerator.Emitに対するエイリアス
    partial class IlUtil
    {
        public static void IL(OpCode opcode, string str) => ilGenerator.Emit(opcode, str);
        public static void IL(OpCode opcode, FieldInfo field) => ilGenerator.Emit(opcode, field);
        public static void IL(OpCode opcode, Label[] labels) => ilGenerator.Emit(opcode, labels);
        public static void IL(OpCode opcode, Label label) => ilGenerator.Emit(opcode, label);
        public static void IL(OpCode opcode, LocalBuilder local) => ilGenerator.Emit(opcode, local);
        public static void IL(OpCode opcode, float arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, byte arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, sbyte arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, short arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, double arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, MethodInfo meth) => ilGenerator.Emit(opcode, meth);
        public static void IL(OpCode opcode, int arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode) => ilGenerator.Emit(opcode);
        public static void IL(OpCode opcode, long arg) => ilGenerator.Emit(opcode, arg);
        public static void IL(OpCode opcode, Type cls) => ilGenerator.Emit(opcode, cls);
        public static void IL(OpCode opcode, SignatureHelper signature) => ilGenerator.Emit(opcode, signature);
        public static void IL(OpCode opcode, ConstructorInfo con) => ilGenerator.Emit(opcode, con);
    }

    partial class IlUtil
    {
        public static LocalBuilder DeclareLocal(Type localType) => ilGenerator.DeclareLocal(localType);
        public static void MarkLabel(Label loc) => ilGenerator.MarkLabel(loc);
        public static Label DefineLabel() => ilGenerator.DefineLabel();
    }

    #endregion
}

Measured memory_bytes: 29248