using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

// (づ°ω°)づﾐe★゜・。。・゜゜・。。・゜☆゜・。。・゜゜・。。・゜
public class Solver
{
    const int MOD = 51123987;
    const int MAX = 50;

    public void Solve()
    {
        int n = ReadInt();
        string s = ReadToken();

        var g = new List<int>();
        for (int i = 0; i < n; i++)
            if (i == n - 1 || s[i] != s[i + 1])
                g.Add(s[i] - 'a');

        int m = g.Count;
        var next = new int[m, 3];
        for (int i = 0; i < m; i++)
            for (int j = 0; j < 3; j++)
            {
                int k;
                for (k = i; k < m; k++)
                    if (g[k] == j)
                        break;
                next[i, j] = k;
            }

        var dp = new int[m + 1, MAX + 2, MAX + 2];
        dp[0, 0, 0] = 1;
        for (int i = 0; i < n; i++)
        {
            var ndp = new int[m + 1, MAX + 2, MAX + 2];
            for (int j = 0; j < m; j++)
                for (int xa = 0; xa <= i && xa <= MAX; xa++)
                    for (int xb = 0; xa + xb <= i && xb <= MAX; xb++)
                    {
                        ndp[next[j, 0], xa + 1, xb] = (ndp[next[j, 0], xa + 1, xb] + dp[j, xa, xb]) % MOD;
                        ndp[next[j, 1], xa, xb + 1] = (ndp[next[j, 1], xa, xb + 1] + dp[j, xa, xb]) % MOD;
                        ndp[next[j, 2], xa, xb] = (ndp[next[j, 2], xa, xb] + dp[j, xa, xb]) % MOD;
                    }
            dp = ndp;
        }

        int ans = 0;
        for (int i = 0; i < m; i++)
            for (int j = n / 3; j < n / 3 + 2; j++)
                for (int k = n / 3; k < n / 3 + 2; k++)
                    if (Math.Abs(j - k) < 2 && Math.Abs(j - (n - j - k)) < 2 && Math.Abs(k - (n - j - k)) < 2)
                        ans = (ans + dp[i, j, k]) % MOD;
        Write(ans);
    }

    #region Main

    protected static TextReader reader;
    protected static TextWriter writer;
    static void Main()
    {
#if DEBUG
        reader = new StreamReader("..\\..\\input.txt");
        //reader = new StreamReader(Console.OpenStandardInput());
        writer = Console.Out;
        //writer = new StreamWriter("..\\..\\output.txt");
#else
        reader = new StreamReader(Console.OpenStandardInput());
        writer = new StreamWriter(Console.OpenStandardOutput());
        //reader = new StreamReader("input.txt");
        //writer = new StreamWriter("output.txt");
#endif
        try
        {
            new Solver().Solve();
            //var thread = new Thread(new Solver().Solve, 1024 * 1024 * 128);
            //thread.Start();
            //thread.Join();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
#if DEBUG
#else
            throw;
#endif
        }
        reader.Close();
        writer.Close();
    }

    #endregion

    #region Read / Write
    private static Queue<string> currentLineTokens = new Queue<string>();
    private static string[] ReadAndSplitLine() { return reader.ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries); }
    public static string ReadToken() { while (currentLineTokens.Count == 0)currentLineTokens = new Queue<string>(ReadAndSplitLine()); return currentLineTokens.Dequeue(); }
    public static int ReadInt() { return int.Parse(ReadToken()); }
    public static long ReadLong() { return long.Parse(ReadToken()); }
    public static double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
    public static int[] ReadIntArray() { return ReadAndSplitLine().Select(int.Parse).ToArray(); }
    public static long[] ReadLongArray() { return ReadAndSplitLine().Select(long.Parse).ToArray(); }
    public static double[] ReadDoubleArray() { return ReadAndSplitLine().Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray(); }
    public static int[][] ReadIntMatrix(int numberOfRows) { int[][] matrix = new int[numberOfRows][]; for (int i = 0; i < numberOfRows; i++)matrix[i] = ReadIntArray(); return matrix; }
    public static int[][] ReadAndTransposeIntMatrix(int numberOfRows)
    {
        int[][] matrix = ReadIntMatrix(numberOfRows); int[][] ret = new int[matrix[0].Length][];
        for (int i = 0; i < ret.Length; i++) { ret[i] = new int[numberOfRows]; for (int j = 0; j < numberOfRows; j++)ret[i][j] = matrix[j][i]; } return ret;
    }
    public static string[] ReadLines(int quantity) { string[] lines = new string[quantity]; for (int i = 0; i < quantity; i++)lines[i] = reader.ReadLine().Trim(); return lines; }
    public static void WriteArray<T>(IEnumerable<T> array) { writer.WriteLine(string.Join(" ", array)); }
    public static void Write(params object[] array) { WriteArray(array); }
    public static void WriteLines<T>(IEnumerable<T> array) { foreach (var a in array)writer.WriteLine(a); }
    private class SDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        public new TValue this[TKey key]
        {
            get { return ContainsKey(key) ? base[key] : default(TValue); }
            set { base[key] = value; }
        }
    }
    private static T[] Init<T>(int size) where T : new() { var ret = new T[size]; for (int i = 0; i < size; i++)ret[i] = new T(); return ret; }
    #endregion
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

// (づ°ω°)づﾐe★゜・。。・゜゜・。。・゜☆゜・。。・゜゜・。。・゜
public class Solver
{
    const int MOD = 1000000009;

    public void Solve()
    {
        int n = ReadInt();
        long ans = 0;
        long prod = 1;
        long v = 1;
        for (int i = 1; i < n; i++)
        {
            if (i > 3 && i % 2 == 0)
            {
                if (i == n - 1)
                {
                    ans = (ans + 2 * (i - 1) * prod) % MOD;
                    break;
                }
                v = (v * 2 + 3) % MOD;
                ans = (ans + 2 * v * prod) % MOD;
                prod = prod * v % MOD;
            }
            else
                ans = (ans + 2 * prod) % MOD;
        }

        ans = ans * ans % MOD;
        ans = (ans + 1) * 2 % MOD;
        Write(ans);
    }

    #region Main

    protected static TextReader reader;
    protected static TextWriter writer;
    static void Main()
    {
#if DEBUG
        reader = new StreamReader("..\\..\\input.txt");
        //reader = new StreamReader(Console.OpenStandardInput());
        writer = Console.Out;
        //writer = new StreamWriter("..\\..\\output.txt");
#else
        reader = new StreamReader(Console.OpenStandardInput());
        writer = new StreamWriter(Console.OpenStandardOutput());
        //reader = new StreamReader("input.txt");
        //writer = new StreamWriter("output.txt");
#endif
        try
        {
            new Solver().Solve();
            //var thread = new Thread(new Solver().Solve, 1024 * 1024 * 128);
            //thread.Start();
            //thread.Join();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
#if DEBUG
#else
            throw;
#endif
        }
        reader.Close();
        writer.Close();
    }

    #endregion

    #region Read / Write
    private static Queue<string> currentLineTokens = new Queue<string>();
    private static string[] ReadAndSplitLine() { return reader.ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries); }
    public static string ReadToken() { while (currentLineTokens.Count == 0)currentLineTokens = new Queue<string>(ReadAndSplitLine()); return currentLineTokens.Dequeue(); }
    public static int ReadInt() { return int.Parse(ReadToken()); }
    public static long ReadLong() { return long.Parse(ReadToken()); }
    public static double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
    public static int[] ReadIntArray() { return ReadAndSplitLine().Select(int.Parse).ToArray(); }
    public static long[] ReadLongArray() { return ReadAndSplitLine().Select(long.Parse).ToArray(); }
    public static double[] ReadDoubleArray() { return ReadAndSplitLine().Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray(); }
    public static int[][] ReadIntMatrix(int numberOfRows) { int[][] matrix = new int[numberOfRows][]; for (int i = 0; i < numberOfRows; i++)matrix[i] = ReadIntArray(); return matrix; }
    public static int[][] ReadAndTransposeIntMatrix(int numberOfRows)
    {
        int[][] matrix = ReadIntMatrix(numberOfRows); int[][] ret = new int[matrix[0].Length][];
        for (int i = 0; i < ret.Length; i++) { ret[i] = new int[numberOfRows]; for (int j = 0; j < numberOfRows; j++)ret[i][j] = matrix[j][i]; } return ret;
    }
    public static string[] ReadLines(int quantity) { string[] lines = new string[quantity]; for (int i = 0; i < quantity; i++)lines[i] = reader.ReadLine().Trim(); return lines; }
    public static void WriteArray<T>(IEnumerable<T> array) { writer.WriteLine(string.Join(" ", array)); }
    public static void Write(params object[] array) { WriteArray(array); }
    public static void WriteLines<T>(IEnumerable<T> array) { foreach (var a in array)writer.WriteLine(a); }
    private class SDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        public new TValue this[TKey key]
        {
            get { return ContainsKey(key) ? base[key] : default(TValue); }
            set { base[key] = value; }
        }
    }
    private static T[] Init<T>(int size) where T : new() { var ret = new T[size]; for (int i = 0; i < size; i++)ret[i] = new T(); return ret; }
    #endregion
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;
using Pi = Pair<int, int>;

class Solver
{
    public void Solve(Scanner sc)
    {
        int N, M;
        sc.Make(out N, out M);
        if (N > M + 1) Fail(0);
        if (N == 2) Fail(0);
        ModInt.Build(M + 1);
        ModInt res = 0;

        for(int i = 0; i < N - 2; i++)
        {
            res += ModInt.Comb(N - 3, i);
        }
        res *= ModInt.Comb(M, N - 1);
        WriteLine(res*(N-2));
    }
}

public struct ModInt
{
    //public const long MOD = (int)1e9 + 7;
    public const long MOD = 998244353;
    public long Value { get; set; }
    public ModInt(long n = 0) { Value = n; }
    private static ModInt[] fac;//階乗
    private static ModInt[] inv;//逆数
    private static ModInt[] facinv;//1/(i!)
    public override string ToString() => Value.ToString();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt operator +(ModInt l, ModInt r)
    {
        l.Value += r.Value;
        if (l.Value >= MOD) l.Value -= MOD;
        return l;
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt operator -(ModInt l, ModInt r)
    {
        l.Value -= r.Value;
        if (l.Value < 0) l.Value += MOD;
        return l;
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt operator *(ModInt l, ModInt r) => new ModInt(l.Value * r.Value % MOD);
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt operator /(ModInt l, ModInt r) => l * Pow(r, MOD - 2);
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator long(ModInt l) => l.Value;
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator ModInt(long n)
    {
        n %= MOD; if (n < 0) n += MOD;
        return new ModInt(n);
    }

    public static ModInt Pow(ModInt m, long n)
    {
        if (n == 0) return 1;
        if (n % 2 == 0) return Pow(m * m, n >> 1);
        else return Pow(m * m, n >> 1) * m;
    }

    public static void Build(int n)
    {
        fac = new ModInt[n + 1];
        facinv = new ModInt[n + 1];
        inv = new ModInt[n + 1];
        inv[1] = 1;
        fac[0] = fac[1] = 1;
        facinv[0] = facinv[1] = 1;
        for (var i = 2; i <= n; i++)
        {
            fac[i] = fac[i - 1] * i;
            inv[i] = MOD - inv[MOD % i] * (MOD / i);
            facinv[i] = facinv[i - 1] * inv[i];
        }
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt Fac(ModInt n) => fac[n];
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt Inv(ModInt n) => inv[n];
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt FacInv(ModInt n) => facinv[n];
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt Comb(ModInt n, ModInt r)
    {
        if (n < r) return 0;
        if (n == r) return 1;
        var calc = fac[n];
        calc = calc * facinv[r];
        calc = calc * facinv[n - r];
        return calc;
    }
}
#region Template
public static class Template
{
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        new Solver().Solve(new Scanner());
        Console.Out.Flush();
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) == 1) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) == -1) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    public static T[] Shuffle<T>(this IList<T> A) { T[] rt = A.ToArray(); Random rnd = new Random(); for (int i = rt.Length - 1; i >= 1; i--) swap(ref rt[i], ref rt[rnd.Next(i + 1)]); return rt; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    //public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    //public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    //public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}
public class Pair<T1, T2> : IComparable<Pair<T1, T2>>
{
    public T1 v1;
    public T2 v2;
    public Pair() { }
    public Pair(T1 v1, T2 v2)
    { this.v1 = v1; this.v2 = v2; }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int CompareTo(Pair<T1, T2> p)
    {
        var c = Comparer<T1>.Default.Compare(v1, p.v1);
        if (c == 0)
            c = Comparer<T2>.Default.Compare(v2, p.v2);
        return c;
    }
    public override string ToString() => $"{v1.ToString()} {v2.ToString()}";
    public void Deconstruct(out T1 a, out T2 b) { a = v1; b = v2; }
}

public class Pair<T1, T2, T3> : Pair<T1, T2>, IComparable<Pair<T1, T2, T3>>
{
    public T3 v3;
    public Pair() : base() { }
    public Pair(T1 v1, T2 v2, T3 v3) : base(v1, v2)
    { this.v3 = v3; }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int CompareTo(Pair<T1, T2, T3> p)
    {
        var c = base.CompareTo(p);
        if (c == 0)
            c = Comparer<T3>.Default.Compare(v3, p.v3);
        return c;
    }
    public override string ToString() => $"{base.ToString()} {v3.ToString()}";
    public void Deconstruct(out T1 a, out T2 b, out T3 c) { Deconstruct(out a, out b); c = v3; }
}
#endregion


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Numerics;
using static System.Math;
using static System.Array;
using static AtCoder.Tool;
using static AtCoder.CalcL;
namespace AtCoder
{
    class AC
    {
        //const int MOD = 1000000007;
        const int MOD = 998244353;
        const int INF = int.MaxValue / 2;
        const long SINF = long.MaxValue / 2;
        const double EPS = 1e-8;
        static readonly int[] dI = { 0, 1, 0, -1 };
        static readonly int[] dJ = { 1, 0, -1, 0 };
        static List<List<int>> G = new List<List<int>>();
        //List<List<Edge>> G = new List<List<Edge>>();
        //static List<Edge> E = new List<Edge>();
        static void Main(string[] args)
        {
            //var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
            //Console.SetOut(sw);
            var cin = new Scanner();

            //var t = int.Parse(Console.ReadLine());
            for(var _ = 0; _ < 1; _++)
            {
                var input = cin.ReadSplitInt();
                int n = input[0];
                int m = input[1];
                if (m < n - 1)
                {
                    Console.WriteLine(0);
                    return;
                }
                var md = new Modulo(m + 10, MOD);
                long ans = 0;
                for(var k = n - 1; k <= m; k++)
                {
                    ans += md.Mul(md.Pow(2, n - 3), md.Mul(md.Ncr(k - 1, n - 2), (n - 2)));
                    ans %= MOD;
                }
                Console.WriteLine(ans);
            }

            //Console.Out.Flush();
        }
        struct Edge
        {
            public int from;

            public int to;
            public long dist;
            public Edge(int t, long c)
            {
                from = -1;
                to = t;
                dist = c;
            }
            public Edge(int f, int t, long c)
            {
                from = f;
                to = t;
                dist = c;
            }

        }
    }

    public class Scanner
    {
        public int[] ReadSplitInt()
        {
            return ConvertAll(Console.ReadLine().Split(), int.Parse);
        }
        public long[] ReadSplitLong()
        {
            return ConvertAll(Console.ReadLine().Split(), long.Parse);
        }
        public double[] ReadSplit_Double()
        {
            return ConvertAll(Console.ReadLine().Split(), double.Parse);
        }
    }
    public static class Tool
    {
        static public void Initialize<T>(ref T[] array, T initialvalue)
        {
            for (var i = 0; i < array.Length; i++)
            {
                array[i] = initialvalue;
            }
        }
        static public void Swap<T>(ref T a, ref T b)
        {
            T keep = a;
            a = b;
            b = keep;
        }

        static public void Display<T>(T[,] array2d, int n, int m)
        {
            for (var i = 0; i < n; i++)
            {
                for (var j = 0; j < m; j++)
                {
                    Console.Write($"{array2d[i, j]} ");
                }
                Console.WriteLine();
            }
        }

        static public long LPow(int a, int b)
        {
            return (long)Pow(a, b);
        }
    }
    static public class CalcI
    {
        public static int Gcd(int a, int b)
        {
            if (a * b == 0) { return Max(a, b); }
            return a % b == 0 ? b : Gcd(b, a % b);
        }
        public static int Lcm(int a, int b)
        {
            int g = Gcd(a, b);
            return a / g * b;
        }
        public static int Ceil(int n, int div)
        {
            return (n + div - 1) / div;
        }
    }
    static public class CalcL
    {
        public static long Gcd(long a, long b)
        {
            if (a * b == 0) { return Max(a, b); }
            return a % b == 0 ? b : Gcd(b, a % b);
        }
        public static long Lcm(long a, long b)
        {
            long g = Gcd(a, b);
            return a / g * b;
        }
        public static long Ceil(long n, long div)
        {
            return (n + div - 1) / div;
        }
    }
    class Modulo
    {
        private int M;
        private readonly long[] m_facs;
        public long Mul(long a, long b)
        {
            return ((a * b) % M);
        }
        public Modulo(long n, int m)
        {
            M = m;
            m_facs = new long[n + 1];
            m_facs[0] = 1;
            for (long i = 1; i <= n; ++i)
            {
                m_facs[i] = Mul(m_facs[i - 1], i);
            }
        }
        public long Fac(long n)
        {
            return m_facs[n];
        }
        public long Pow(long a, long m)
        {
            switch (m)
            {
                case 0:
                    return 1L;
                case 1:
                    return a;
                default:
                    long p1 = Pow(a, m / 2);
                    long p2 = Mul(p1, p1);
                    return ((m % 2) == 0) ? p2 : Mul(p2, a);
            }
        }
        public long Div(long a, long b)
        {
            return Mul(a, Pow(b, M - 2));
        }
        public long Ncr(long n, long r)
        {
            if (n < r) { return 0; }
            if (n == r) { return 1; }
            long res = Fac(n);
            res = Div(res, Fac(r));
            res = Div(res, Fac(n - r));
            return res;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{

    class Program
    {

        static bool def(List<long> mas, List<long> an, int ind)
        {
            var cnt = 0;
            for(int i=ind, j =  0; i < mas.Count && j < an.Count ; i++, j++)
            {
                if (mas[i] != an[j])
                    return false;
                cnt++;
            }
            if (cnt < an.Count)
                return false;
            for (int i = 0; i < ind; i++)
                if (mas[i] == 0)
                    return false;
            for (int i = ind + an.Count; i < mas.Count; i++)
                if (mas[i] == 0)
                    return false;
            if (an[an.Count - 1] == 0 && ind + an.Count >= mas.Count)
                return false;
            if (an[0] == 0 && ind == 0)
                return false;
            return true;
        }

        static async Task Main(string[] args)
        {
            var input = Console.ReadLine().Split(' ').Select(long.Parse).ToList();
            long x = input[0], y = input[1];
            if( x == y)
            {
                Console.WriteLine("YES");
            }
            else
            {
                var xmas = new List<long>();
                while(x > 0)
                {
                    xmas.Add(x % 2);
                    x /= 2;
                }
                xmas.Reverse();

                var ymas = new List<long>();
                while (y > 0)
                {
                    ymas.Add(y % 2);
                    y /= 2;
                }
                ymas.Reverse();

                var rxmas = xmas.Select(i => i).ToList();
                rxmas.Reverse();
                var gmas = xmas.Select(i => i).ToList();
                for (int i = gmas.Count() - 1; i >= 0; i--)
                    if (gmas[i] == 0)
                        gmas.RemoveAt(i);
                    else
                        break;
                var rgmas = gmas.Select(i => i).ToList();
                rgmas.Reverse();

                var good = true;
                for(int i=0;i < ymas.Count(); i++)
                {
                    if (def(ymas, xmas, i))
                    {
                        Console.WriteLine("YES");
                        return;
                    }
                    if (def(ymas, rxmas, i))
                    {
                        Console.WriteLine("YES");
                        return;
                    }
                    if (def(ymas, gmas, i))
                    {
                        Console.WriteLine("YES");
                        return;
                    }
                    if (def(ymas, rgmas, i))
                    {
                        Console.WriteLine("YES");
                        return;
                    }
                }
                Console.WriteLine("NO");
            }
        }
    }
}


using System;
using System.Linq;
using CompLib.Util;
using System.Threading;
using System.IO;
using System.Collections.Generic;
using System.Text;

public class Program
{
    long X, Y;
    public void Solve()
    {
        var sc = new Scanner();
        X = sc.NextLong();
        Y = sc.NextLong();

        if (X == Y)
        {
            Console.WriteLine("YES");
            return;
        }

        bool[] by = ToArray(Y);

        bool[] bx1 = ToArray(2 * X + 1);
        if (F(by, bx1))
        {
            Console.WriteLine("YES");
            return;
        }
        Array.Reverse(bx1);
        if (F(by, bx1))
        {
            Console.WriteLine("YES");
            return;
        }

        while (X % 2 == 0) X /= 2;
        bool[] bx2 = ToArray(X);
        if (F(by, bx2))
        {
            Console.WriteLine("YES");
            return;
        }
        Array.Reverse(bx2);
        if (F(by, bx2))
        {
            Console.WriteLine("YES");
            return;
        }

        Console.WriteLine("NO");
    }

    bool F(bool[] fy, bool[] fx)
    {
        if (fy.Length < fx.Length) return false;
        bool left = true;
        for (int i = 0; i + fx.Length <= fy.Length && left; i++)
        {
            bool flag = true;
            for (int j = 0; j < fx.Length && flag; j++)
            {
                flag &= fx[j] == fy[i + j];
            }
            if (flag)
            {
                for (int j = i + fx.Length; j < fy.Length && flag; j++)
                {
                    flag &= fy[j];
                }
                if (flag) return true;
            }

            left &= fy[i];
        }
        return false;
    }

    bool[] ToArray(long n)
    {
        int lg = 0;
        long tmp = n;
        while (tmp > 0)
        {
            lg++;
            tmp /= 2;
        }

        tmp = n;
        bool[] ret = new bool[lg];
        for (int i = 0; i < lg; i++)
        {
            ret[i] = tmp % 2 == 1;
            tmp /= 2;
        }
        return ret;
    }

    public static void Main(string[] args) => new Program().Solve();
    // public static void Main(string[] args) => new Thread(new Program().Solve, 1 << 27).Start();
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            if (_index >= _line.Length)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                } while (s.Length == 0);

                _line = s.Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public string ReadLine()
        {
            _index = _line.Length;
            return Console.ReadLine();
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            string s = Console.ReadLine();
            _line = s.Length == 0 ? new string[0] : s.Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


//

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;

namespace sar_cs
{
    static class Helpers
    {
        // Mono lacks this override of string.Join()
        public static string Join<T>(this IEnumerable<T> objs, string sep)
        {
            var sb = new StringBuilder();
            foreach (var s in objs) { if (sb.Length != 0) sb.Append(sep); sb.Append(s); }
            return sb.ToString();
        }

        public static string AsString(this double a, int digits)
        {
            return a.ToString("F" + digits, CultureInfo.InvariantCulture);
        }

        public static Stopwatch SW = Stopwatch.StartNew();
    }

    public class Program
    {
        #region Helpers

        public class Parser
        {
            const int BufSize = 8000;
            TextReader s;
            char[] buf = new char[BufSize];
            int bufPos;
            int bufDataSize;
            bool eos;   // eos read to buffer
            int lastChar = -2;
            int nextChar = -2;
            StringBuilder sb = new StringBuilder();

            void FillBuf()
            {
                if (eos) return;
                bufDataSize = s.Read(buf, 0, BufSize);
                if (bufDataSize == 0) eos = true;
                bufPos = 0;
            }

            int Read()
            {
                if (nextChar != -2)
                {
                    lastChar = nextChar;
                    nextChar = -2;
                }
                else
                {
                    if (bufPos == bufDataSize) FillBuf();
                    if (eos) lastChar = -1;
                    else lastChar = buf[bufPos++];
                }
                return lastChar;
            }

            void Back()
            {
                if (lastChar == -2) throw new Exception();  // no chars were ever read
                nextChar = lastChar;
            }

            public Parser()
            {
                s = Console.In;
            }

            public int ReadInt()
            {
                int res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public long ReadLong()
            {
                long res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-')
                {
                    sign = 1;
                    c = Read();
                }
                int len = 0;

                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public int ReadLnInt()
            {
                int res = ReadInt(); ReadLine(); return res;
            }

            public long ReadLnLong()
            {
                long res = ReadLong(); ReadLine(); return res;
            }

            public double ReadDouble()
            {
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));

                int sign = 1;
                if (c == '-') { sign = -1; c = Read(); }

                sb.Length = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    sb.Append((char)c); c = Read();
                }

                Back();
                var res = double.Parse(sb.ToString(), CultureInfo.InvariantCulture);
                return res * sign;
            }

            public string ReadLine()
            {
                int c = Read();
                sb.Length = 0;
                while (c != -1 && c != 13 && c != 10)
                {
                    sb.Append((char)c); c = Read();
                }
                if (c == 13) { c = Read(); if (c != 10) Back(); }
                return sb.ToString();
            }

            public bool SeekEOF
            {
                get
                {
                L0:
                    int c = Read();
                    if (c == -1) return true;
                    if (char.IsWhiteSpace((char)c)) goto L0;
                    Back();
                    return false;
                }
            }

            public int[] ReadIntArr(int n)
            {
                var a = new int[n]; for (int i = 0; i < n; i++) a[i] = ReadInt(); return a;
            }

            public long[] ReadLongArr(int n)
            {
                var a = new long[n]; for (int i = 0; i < n; i++) a[i] = ReadLong(); return a;
            }
        }

        static void pe() { Console.WriteLine("???"); Environment.Exit(0); }
        static void re() { Environment.Exit(55); }
        static void Swap<T>(ref T a, ref T b) { T t = a; a = b; b = t; }
        static int Sqr(int a) { return a * a; }
        static long SqrL(int a) { return (long)a * a; }
        static long Sqr(long a) { return a * a; }
        static double Sqr(double a) { return a * a; }

        static StringBuilder sb = new StringBuilder();
        static Random rand = new Random(5);

        #endregion Helpers

        static void Main(string[] args)
        {
            checked
            {
                //Console.SetIn(File.OpenText("_input"));
                var parser = new Parser();
                while (!parser.SeekEOF)
                {
                    double a = parser.ReadDouble();
                    double b = parser.ReadDouble();
                    double m = parser.ReadDouble();
                    double vx = parser.ReadDouble();
                    double vy = parser.ReadDouble();
                    double vz = parser.ReadDouble();

                    double t = m / -vy;
                    double t_bak = t;

                    /* x result */
                    double x = a / 2;
                    if (vx != 0)
                    {
                        while (t > 0)
                        {
                            if (vx < 0)
                            {
                                double tt = x / -vx;
                                if (t <= tt)
                                {
                                    x += t * vx;
                                    break;
                                }
                                else
                                {
                                    x = 0;
                                    vx = -vx;
                                    t -= tt;
                                }
                            }
                            else
                            {
                                double tt = (a - x) / vx;
                                if (t <= tt)
                                {
                                    x += t * vx;
                                    break;
                                }
                                else
                                {
                                    x = a;
                                    vx = -vx;
                                    t -= tt;
                                }
                            }
                        }
                    }

                    /* z result */
                    t = t_bak;
                    double z = 0;
                    if (vz != 0)
                    {
                        while (t > 0)
                        {
                            if (vz < 0)
                            {
                                double tt = z / -vz;
                                if (t <= tt)
                                {
                                    z += t * vz;
                                    break;
                                }
                                else
                                {
                                    z = 0;
                                    vz = -vz;
                                    t -= tt;
                                }
                            }
                            else
                            {
                                double tt = (b - z) / vz;
                                if (t <= tt)
                                {
                                    z += t * vz;
                                    break;
                                }
                                else
                                {
                                    z = b;
                                    vz = -vz;
                                    t -= tt;
                                }
                            }
                        }
                    }

                    Console.WriteLine(x.AsString(10) + " " + z.AsString(10));
                }
            }
        }

        //static void Main(string[] args)
        //{
        //    new Thread(Main2, 50 << 20).Start();
        //}
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace CodeForces
{
    class Program
    {
        struct Matrix
        {
            public long A1, A2, A3, A4;
            const long BMod = 10000000;

            public static long MulMod(long a, long b, long mod)
            {
                if (a == 0 || b == 0)
                    return 0;
                else if (long.MaxValue / b > a)
                    return (a * b) % mod;
                else
                {
                    long j0 = a / BMod;
                    long j1 = a % BMod;
                    long k0 = b / BMod;
                    long k1 = b % BMod;
                    long r = (((j0 * k1 + j1 * k0) % (mod / BMod)) * BMod + j1 * k1) % mod;
                    return r;
                }
            }

            public static Matrix MulMod(Matrix m1, Matrix m2, long mod)
            {
                Matrix result = new Matrix();
                result.A1 = (MulMod(m1.A1, m2.A1, mod) + MulMod(m1.A2, m2.A3, mod)) % mod;
                result.A2 = (MulMod(m1.A1, m2.A2, mod) + MulMod(m1.A2, m2.A4, mod)) % mod;
                result.A3 = (MulMod(m1.A3, m2.A1, mod) + MulMod(m1.A4, m2.A3, mod)) % mod;
                result.A4 = (MulMod(m1.A3, m2.A2, mod) + MulMod(m1.A4, m2.A4, mod)) % mod;
                return result;
            }

            public void Mod(long mod)
            {
                A1 %= mod; A2 %= mod; A3 %= mod; A4 %= mod;
            }

            public static Matrix PowerMod(long power, Matrix m, long mod)
            {
                Matrix modLast = m, result = new Matrix();

                while (power != 0)
                {
                    if ((power & 1) == 1)
                    {
                        if (result.A1 == 0)
                            result = modLast;
                        else
                            result = Matrix.MulMod(result, modLast, mod);
                    }

                    modLast = Matrix.MulMod(modLast, modLast, mod);
                    power >>= 1;
                }

                return result;
            }
        }

        struct Result
        {
            public long Index, Value;
            public int SIndex;
        }

        static void Main(string[] args)
        {
            long[] steps = new long[] { 60, 300, 1500, 15000, 150000, 1500000, 15000000, 150000000, 1500000000, 15000000000, 150000000000, 1500000000000, 15000000000000, 150000000000000, 1500000000000000, 15000000000000000 };
            long[] mods = new long[] { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000 };

            long n = Convert.ToInt64(Console.ReadLine());

            if (n == 1)
            {
                Console.WriteLine(1);
                return;
            }

            int index = 0;
            long modValue = n % mods[index];

            Queue<Result> queue = new Queue<Result>();
            long[] fs = new long[60]; fs[1] = 1;

            for (int i = 2; i < fs.Length; i++)
            {
                fs[i] = (fs[i - 1] + fs[i - 2]) % mods[index];
                if (fs[i] == modValue)
                    queue.Enqueue(new Result { Index = i, Value = fs[i], SIndex = index });
            }

            List<Result> result = new List<Result>();

            while (queue.Count > 0)
            {
                Result current = queue.Dequeue();

                if (current.Value == n && current.SIndex == 12)
                {
                    result.Add(current);
                    continue;
                }
                else if (current.SIndex > 12)
                    continue;

                for (int i = 0; i * steps[current.SIndex] < steps[current.SIndex + 1]; i++)
                {
                    long nIndex = i * steps[current.SIndex] + current.Index;
                    long value = Cal(nIndex, mods[current.SIndex + 1]);

                    if (value == n % mods[current.SIndex + 1])
                        queue.Enqueue(new Result { Index = nIndex, SIndex = current.SIndex + 1, Value = value });
                }
            }

            if (result.Count == 0)
                Console.WriteLine(-1);
            else
            {
                long sIndex = result.Select(x => x.Index).Min();
                Console.WriteLine(sIndex);
            }
        }

        static long Cal(long index, long mod)
        {
            Matrix m = new Matrix { A1 = 1, A2 = 1, A3 = 1, A4 = 0 };
            Matrix power = Matrix.PowerMod(index, m, mod);
            return (long)power.A2;
        }
    }
}


using System;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Codeforces
{
    public class Input
    {
        private static string _line;

        public static bool Next()
        {
            _line = Console.ReadLine();
            return _line != null;
        }

        public static bool Next(out long a)
        {
            var ok = Next();
            a = ok ? long.Parse(_line) : 0;
            return ok;
        }

        public static bool Next(out long a, out long b)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
            }
            else
            {
                a = b = 0;
            }

            return ok;
        }

        public static bool Next(out long a, out long b, out long c)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
            }
            else
            {
                a = b = c = 0;
            }
            return ok;
        }

        public static bool Next(out long a, out long b, out long c, out long d)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
            }
            else
            {
                a = b = c = d = 0;
            }
            return ok;
        }

        public static bool Next(out long a, out long b, out long c, out long d, out long e)
        {
            var ok = Next();
            if (ok)
            {
                var array = _line.Split(' ').Select(long.Parse).ToArray();
                a = array[0];
                b = array[1];
                c = array[2];
                d = array[3];
                e = array[4];
            }
            else
            {
                a = b = c = d = e = 0;
            }
            return ok;
        }

        public static List<long> Numbers()
        {
            return !Next() ? new List<long>() : _line.Split(' ').Select(long.Parse).ToList();
        }

        public static bool Next(out string value)
        {
            value = string.Empty;
            if (!Next()) return false;
            value = _line;
            return true;
        }
    }

    public class DisjointSet
    {
        private readonly int[] _parent;
        private readonly int[] _rank;
        public int Count { get; private set; }

        public DisjointSet(int count, int initialize = 0)
        {
            Count = count;
            _parent = new int[Count];
            _rank = new int[Count];
            for (var i = 0; i < initialize; i++) _parent[i] = i;
        }

        private DisjointSet(int count, int[] parent, int[] rank)
        {
            Count = count;
            _parent = parent;
            _rank = rank;
        }

        public void Add(int v)
        {
            _parent[v] = v;
            _rank[v] = 0;
        }

        public int Find_Recursive(int v)
        {
            if (_parent[v] == v) return v;
            return _parent[v] = Find(_parent[v]);
        }

        public int Find(int v)
        {
            if (_parent[v] == v) return v;
            var last = v;
            while (_parent[last] != last) last = _parent[last];
            while (_parent[v] != v)
            {
                var t = _parent[v];
                _parent[v] = last;
                v = t;
            }
            return last;
        }

        public int this[int v]
        {
            get { return Find(v); }
            set { Union(v, value); }
        }

        public void Union(int a, int b)
        {
            a = Find(a);
            b = Find(b);
            if (a == b) return;
            if (_rank[a] < _rank[b])
            {
                var t = _rank[a];
                _rank[a] = _rank[b];
                _rank[b] = t;
            }
            _parent[b] = a;
            if (_rank[a] == _rank[b]) _rank[a]++;
        }

        public int GetSetCount()
        {
            var result = 0;
            for (var i = 0; i < Count; i++)
            {
                if (_parent[i] == i) result++;
            }
            return result;
        }

        public DisjointSet Clone()
        {
            var rank = new int[Count];
            _rank.CopyTo(rank, 0);
            var parent = new int[Count];
            _parent.CopyTo(parent, 0);
            return new DisjointSet(Count, parent, rank);
        }

        public override string ToString()
        {
            return string.Join(",", _parent.Take(50));
        }
    }

    public class Matrix
    {
        public static Matrix Create(int i, int j)
        {
            var m = new Matrix(i, j);
            return m;
        }

        public static Matrix Create(int i)
        {
            var m = new Matrix(i, i);
            return m;
        }

        public long Modulo { get; set; }

        private readonly int _width;
        private readonly int _height;
        private readonly long[,] _data;
        public int Width{get { return _width; }}
        public int Height{get { return _height; }}

        private Matrix(int i, int j)
        {
            Modulo = 1000000000000000000L;
            _width = j;
            _height = i;
            _data = new long[_height, _width];
        }

        public static Matrix operator *(Matrix m1, Matrix m2)
        {
            if (m1.Width != m2.Height) throw new InvalidDataException("m1.Width != m2.Height");
            var m = Create(m2.Width, m1.Height);
            m.Modulo = m1.Modulo;
            for (var i=0;i<m2.Width;i++)
                for (var j = 0; j < m1.Height; j++)
                {
                    for (var k = 0; k < m1.Width; k++)
                        m[j, i] += (m1[j, k]*m2[k, i]) % m1.Modulo;
                    m[j, i] %= m1.Modulo;
                }
            return m;
        }

        public static Matrix operator +(Matrix m1, Matrix m2)
        {
            var m = m1.Clone();
            for (var i=0;i<m2.Width;i++)
                for (var j = 0; j < m1.Height; j++)
                    m[j, i] = (m[j, i] + m2[j, i]) % m1.Modulo;
            return m;
        }

        public static Matrix operator -(Matrix m1, Matrix m2)
        {
            var m = m1.Clone();
            for (var i=0;i<m2.Width;i++)
                for (var j = 0; j < m1.Height; j++)
                    m[j, i] = (m[j, i] - m2[j, i]) % m1.Modulo;
            return m;
        }

        public static Matrix operator *(Matrix m1, long l)
        {
            var m = m1.Clone();
            for (var i=0;i<m1.Width;i++)
                for (var j=0;j<m1.Height;j++)
                        m[j, i] = (m[j, i] * l) % m.Modulo;
            return m;
        }

        public static Matrix operator *(long l, Matrix m1)
        {
            return m1*l;
        }

        public static Matrix operator +(Matrix m1, long l)
        {
            var m = m1.Clone();
            for (var i = 0; i < m1.Width; i++)
                    m[i, i] = (m[i, i] + l) % m.Modulo;
            return m;
        }

        public static Matrix operator +(long l, Matrix m1)
        {
            return m1 + l;
        }

        public static Matrix operator -(Matrix m1, long l)
        {
            return m1 + (-l);
        }

        public static Matrix operator -(long l, Matrix m1)
        {
            var m = m1.Clone() * -1;
            return m + l;
        }

        public Matrix BinPower(long l)
        {
            var n = 1;
            var m = Clone();
            var result = new Matrix(m.Height, m.Width) + 1;
            result.Modulo = m.Modulo;
            while (l != 0)
            {
                var i = l & ~(l - 1);
                l -= i;
                while (n < i)
                {
                    m = m*m;
                    n <<= 1;
                }
                result *= m;
            }
            return result;
        }

        public void Fill(long l)
        {
            l %= Modulo;
            for (var i = 0; i < _height; i++)
                for (var j = 0; j < _width; j++)
                    _data[i, j] = l;
        }

        public Matrix Clone()
        {
            var m = new Matrix(_width, _height);
            Array.Copy(_data, m._data, _data.Length);
            m.Modulo = Modulo;
            return m;
        }

        public long this[int i, int j]
        {
            get { return _data[i, j]; }
            set { _data[i, j] = value % Modulo; }
        }
    }

    public class RedBlackTree<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
    {
        private readonly List<Node> _items = new List<Node>();
        private Node _root;
        private readonly IComparer<TKey> _comparer;

        public RedBlackTree()
        {
            _comparer = Comparer<TKey>.Default;
        }

        private Node _search(TKey key)
        {
            var node = _root;
            while (node != null)
            {
                var i = _comparer.Compare(key, node.Key);
                if (i == 0) return node;
                node = i > 0 ? node.Right : node.Left;
            }
            return null;
        }

        public void Add(TKey key, TValue value)
        {
            var node = new Node { Key = key, Value = value, Red = true };
            _items.Add(node);
            if (_root == null)
            {
                _root = node;
                _root.Red = false;
                return;
            }
            var x = _root;
            Node y = null;
            while (x != null)
            {
                y = x;
                x = _comparer.Compare(key, x.Key) > 0 ? x.Right : x.Left;
            }
            node.Parent = y;
            if (y == null) _root = node;
            else if (_comparer.Compare(key, y.Key) > 0) y.Right = node;
            else y.Left = node;
            InsertFixup(node);
        }

        private void InsertFixup(Node node)
        {
            while (true)
            {
                var parent = node.Parent;
                if (parent == null || !parent.Red || parent.Parent == null) break;
                var grand = parent.Parent;

                if (parent == grand.Left)
                {
                    var uncle = grand.Right;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Right) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateLeft(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateRight(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
                else
                {
                    if (_root.Parent != null) Debugger.Break();

                    var uncle = grand.Left;
                    if (uncle != null && uncle.Red) /* parent and uncle both red: we could swap colors of them with grand */
                    {
                        parent.Red = false;
                        uncle.Red = false;
                        grand.Red = true;
                        node = grand; /* and continue fixing tree for grand */
                    }
                    else if (node == parent.Left) /* we rotate around the parent */
                    {
                        node = parent;
                        RotateRight(node);
                    }
                    else /* rotate around grand and switch colors of grand and parent */
                    {
                        parent.Red = false;
                        grand.Red = true;
                        RotateLeft(grand);
                        if (grand == _root) _root = parent;
                        break; /* and finish since tree is fixed */
                    }
                }
            }
            _root.Red = false;
        }

        private void RotateLeft(Node node)
        {
            if (node.Right == null)
                throw new NotSupportedException("Cannot rotate left: right node is missing");
            var parent = node.Parent;
            var right = node.Right;
            right.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = right;
                else parent.Right = right;
            }
            node.Right = right.Left;
            if (node.Right != null)
                node.Right.Parent = node;
            right.Left = node;
            node.Parent = right;
        }

        private void RotateRight(Node node)
        {
            if (node.Left == null)
                throw new NotSupportedException("Cannot rotate right: left node is missing");
            var parent = node.Parent;
            var left = node.Left;
            left.Parent = parent;
            if (parent != null)
            {
                if (parent.Left == node) parent.Left = left;
                else parent.Right = left;
            }
            node.Left = left.Right;
            if (node.Left != null)
                node.Left.Parent = node;
            left.Right = node;
            node.Parent = left;
        }

        public void Remove(TKey key)
        {
            var node = _search(key);
            if (node == null) return;

            var deleted = node.Left == null || node.Right == null ? node : Next(node);
            var child = deleted.Left ?? deleted.Right;
            if (child != null)
                child.Parent = deleted.Parent;
            if (deleted.Parent == null) /* root */
                _root = child;
            else if (deleted == deleted.Parent.Left)
                deleted.Parent.Left = child;
            else
                deleted.Parent.Right = child;
            if (node != deleted)
            {
                node.Key = deleted.Key;
                node.Value = deleted.Value;
            }

            if (!deleted.Red)
            {
                DeleteFixup(child, deleted.Parent);
            }
        }

        private void DeleteFixup(Node node, Node parent)
        {
            while (parent != null && (node == null || !node.Red))
            {
                if (node == parent.Left)
                {
                    var brother = parent.Right;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateLeft(parent);
                        brother = parent.Right;
                    }
                    if ((brother.Left == null || !brother.Left.Red) && (brother.Right == null || !brother.Right.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Right == null || !brother.Right.Red)
                        {
                            if (brother.Left != null)
                                brother.Left.Red = false;
                            brother.Red = true;
                            RotateRight(brother);
                            brother = parent.Right;
                        }
                        if (!brother.Right.Red) Debugger.Break();
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Right != null)
                            brother.Right.Red = false;
                        RotateLeft(parent);
                        node = _root;
                    }
                }
                else
                {
                    var brother = parent.Left;
                    if (brother.Red)
                    {
                        brother.Red = false;
                        parent.Red = true;
                        RotateRight(parent);
                        brother = parent.Left;
                    }
                    if ((brother.Right == null || !brother.Right.Red) && (brother.Left == null || !brother.Left.Red))
                    {
                        node = parent.Red ? _root : parent;
                        brother.Red = true;
                        parent.Red = false;
                    }
                    else
                    {
                        if (brother.Left == null || !brother.Left.Red)
                        {
                            if (brother.Right != null)
                                brother.Right.Red = false;
                            brother.Red = true;
                            RotateLeft(brother);
                            brother = parent.Left;
                        }
                        brother.Red = parent.Red;
                        parent.Red = false;
                        if (brother.Left != null)
                            brother.Left.Red = false;
                        RotateRight(parent);
                        node = _root;
                    }
                }
                parent = node.Parent;
            }

            node.Red = false;
        }

        public TValue this[TKey key]
        {
            get
            {
                var node = _search(key);
                return node == null ? default(TValue) : node.Value;
            }
            set
            {
                var node = _search(key);
                if (node == null) Add(key, value);
                else node.Value = value;
            }
        }

        public bool Check()
        {
            int count;
            return _root == null || _root.Check(out count);
        }

        private Node Minimum(Node node)
        {
            var result = node;
            while (result.Left != null) result = result.Left;
            return result;
        }

        private Node Maximum(Node node)
        {
            var result = node;
            while (result.Right != null) result = result.Right;
            return result;
        }

        private Node Next(Node node)
        {
            Node result;
            if (node.Right != null)
            {
                result = Minimum(node.Right);
            }
            else
            {
                while (node.Parent != null && node.Parent.Right == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        private Node Previous(Node node)
        {
            Node result;
            if (node.Left != null)
            {
                result = Maximum(node.Left);
            }
            else
            {
                while (node.Parent != null && node.Parent.Left == node)
                    node = node.Parent;
                result = node.Parent;
            }
            return result;
        }

        public void Print()
        {
            var node = Minimum(_root);
            while (node != null)
            {
                Console.Write(node.Red ? '*' : ' ');
                Console.Write(node.Key);
                Console.Write('\t');
                node = Next(node);
            }
        }

        #region Nested classes

        [DebuggerDisplay("{ToString()}")]
        private sealed class Node
        {
            public TKey Key;
            public TValue Value;
            public bool Red;

            public Node Parent;
            public Node Left;
            public Node Right;

            public bool Check(out int count)
            {
                count = 0;
                if (Red)
                {
                    if (Left != null && Left.Red) return false;
                    if (Right != null && Right.Red) return false;
                }
                var left = 0;
                var right = 0;
                if (Left != null && !Left.Check(out left)) return false;
                if (Right != null && !Right.Check(out right)) return false;
                count = left + (Red ? 0 : 1);
                if (left != right)
                {
                    ;
                }
                return left == right;
            }

            public override string ToString()
            {
                return ToShortString(2);
            }

            private string ToShortString(int depth = 0)
            {
                return depth == 0
                    ? string.Format("{0}{1}", Red ? "*" : "", Key)
                    : string.Format("({1}<{0}>{2})", ToShortString(), Left == null ? "null" : Left.ToShortString(depth - 1), Right == null ? "null" : Right.ToShortString(depth - 1));
            }
        }

        #endregion

        #region IEnumerable

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }
}

namespace Codeforces.TaskC
{
    public class Task
    {
        public static void Main()
        {
            var task = new Task();
            try
            {
                task.Solve();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(ex);
                throw;
            }
        }

        /*abacabadabacaba*/

        private long MaxSubstring(long best, long l, long r, long a, long b, int ch)
        {
            if (l > r || a > b) return best;
            if ((best >= b - a + 1) || (best >= r - l + 1)) return best;

            best = Math.Max(best, Math.Min(r, b) - Math.Max(l, a) + 1);
            var divide = 1 << ch;
             bool f1 = false, f2 = false;

            if (r == divide && l == r) return best;
            if (a == divide && a == b) return best;

            if (l == divide) l++;
            if (r == divide) r--;
            if (a == divide) a++;
            if (b == divide) b--;

            if (l > divide)
            {
                l -= divide;
                f1 = true;
            }
            if (r > divide)
            {
                r -= divide;
                f1 = !f1;
            }
            if (a > divide)
            {
                a -= divide;
                f2 = true;
            }
            if (b > divide)
            {
                b -= divide;
                f2 = !f2;
            }

            if (--ch >= 0)
            {
                if (f1 && f2) best = Math.Max(Math.Max(best, divide - Math.Max(l, a)), Math.Min(r, b));
                if (f1 && !f2)
                {
                    best = MaxSubstring(best, l, divide - 1, a, b, ch);
                    best = MaxSubstring(best, 1, r, a, b, ch);
                }
                if (!f1 && f2)
                {
                    best = MaxSubstring(best, l, r, a, divide - 1, ch);
                    best = MaxSubstring(best, l, r, 1, b, ch);
                }
                if (!f1 && !f2) best = MaxSubstring(best, l, r, a, b, ch);
                if (f1 && f2)
                {
                    best = MaxSubstring(best, 1, r, a, divide - 1, ch);
                    best = MaxSubstring(best, l, divide - 1, 1, b, ch);
                }
            }

            return best;
        }

        void Solve()
        {
            long l1, r1, l2, r2;
            Input.Next(out l1, out r1, out l2, out r2);
            var n = MaxSubstring(0, l1, r1, l2, r2, 29);
            Console.WriteLine(n);
        }
    }
}


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
//using DebugerVisualizer;

namespace _105
{
	[Serializable]
	class VNodeValue<TNode>
	{
		public int Version;
		public TNode Value;

		public VNodeValue(int version)
		{
			Version = version;
		}
	}

	[Serializable]
	class VEdge<TNode, TEdge>
		where TNode : ICloneable
	{
		public TEdge Value;
		public VNode<TNode, TEdge> Source;
		public VNode<TNode, TEdge> Target;

		public override string ToString()
		{
			return Value.ToString();
		}
	}

	[Serializable]
	class VEdges<TNode, TEdge>
		where TNode : ICloneable
	{
		public int Version;
		public List<VEdge<TNode, TEdge>> Edges = new List<VEdge<TNode, TEdge>>();

		public VEdges(int version)
		{
			Version = version;
		}
	}

	[Serializable]
	class VNode<TNode, TEdge>
		where TNode : ICloneable
	{
		VGraph<TNode, TEdge> _graph;

		Stack<VNodeValue<TNode>> _valueStack = new Stack<VNodeValue<TNode>>();
		Stack<VEdges<TNode, TEdge>> _ins = new Stack<VEdges<TNode, TEdge>>();
		Stack<VEdges<TNode, TEdge>> _outs = new Stack<VEdges<TNode, TEdge>>();

		internal VNode(VGraph<TNode, TEdge> graph, TNode nodeValue)
		{
			_graph = graph;
			var newNode = new VNodeValue<TNode>(_graph.Version);
			newNode.Value = nodeValue;
			_valueStack.Push(newNode);
			_ins.Push(new VEdges<TNode, TEdge>(_graph.Version));
			_outs.Push(new VEdges<TNode, TEdge>(_graph.Version));
		}

		public TNode ReadValue
		{
			get { return _valueStack.Peek().Value; }
		}

		public TNode WriteValue
		{
			get
			{
				if (_valueStack.Peek().Version != _graph.Version)
				{
					var newVersion = new VNodeValue<TNode>(_graph.Version);
					if (_valueStack.Peek().Value != null)
						newVersion.Value = (TNode)_valueStack.Peek().Value.Clone();
					_valueStack.Push(newVersion);
					_graph.SheduleValueRollback(this);
				}

				return _valueStack.Peek().Value;
			}
		}

		public IEnumerable<VEdge<TNode, TEdge>> Ins
		{
			get
			{
				foreach (var edges in _ins)
					foreach (var e in edges.Edges)
					{
						yield return e;
					}
			}
		}

		public IEnumerable<VEdge<TNode, TEdge>> Outs
		{
			get
			{
				foreach (var edges in _outs)
					foreach (var e in edges.Edges)
					{
						yield return e;
					}
			}
		}

		public void AddIns(VEdge<TNode, TEdge> edge)
		{
			if (_ins.Peek().Version != _graph.Version)
			{
				var newVersion = new VEdges<TNode, TEdge>(_graph.Version);
				_ins.Push(newVersion);
				_graph.SheduleInEdgeRollback(this);
			}
			_ins.Peek().Edges.Add(edge);
		}

		public void AddOut(VEdge<TNode, TEdge> edge)
		{
			if (_outs.Peek().Version != _graph.Version)
			{
				var newVersion = new VEdges<TNode, TEdge>(_graph.Version);
				_outs.Push(newVersion);
				_graph.SheduleOutEdgeRollback(this);
			}
			_outs.Peek().Edges.Add(edge);
		}

		internal void RollbackValue()
		{
			_valueStack.Pop();
		}

		internal void RollbackIns()
		{
			_ins.Pop();
		}

		internal void RollbackOuts()
		{
			_outs.Pop();
		}

		public override string ToString()
		{
			return ReadValue.ToString();
		}
	}

	[Serializable]
	class VGraph<TNode, TEdge> : IDisposable
		where TNode : ICloneable
	{
		public int Version = 0;

		Stack<List<VNode<TNode, TEdge>>> _valueRollbacks = new Stack<List<VNode<TNode, TEdge>>>();
		Stack<List<VNode<TNode, TEdge>>> _inEdgeRollbacks = new Stack<List<VNode<TNode, TEdge>>>();
		Stack<List<VNode<TNode, TEdge>>> _outEdgeRollbacks = new Stack<List<VNode<TNode, TEdge>>>();

		public VGraph()
		{
			StartTransaction();
		}

		public void SheduleValueRollback(VNode<TNode, TEdge> node)
		{
			_valueRollbacks.Peek().Add(node);
		}

		public void SheduleInEdgeRollback(VNode<TNode, TEdge> node)
		{
			_inEdgeRollbacks.Peek().Add(node);
		}

		public void SheduleOutEdgeRollback(VNode<TNode, TEdge> node)
		{
			_outEdgeRollbacks.Peek().Add(node);
		}

		public VNode<TNode, TEdge> AddNode(TNode nodeValue)
		{
			return new VNode<TNode, TEdge>(this, nodeValue);
		}

		public void AddEdge(VNode<TNode, TEdge> a, VNode<TNode, TEdge> b, TEdge edgeValue)
		{
			var newEdge = new VEdge<TNode, TEdge>();
			newEdge.Source = a;
			newEdge.Target = b;
			newEdge.Value = edgeValue;

			a.AddOut(newEdge);
			b.AddIns(newEdge);
		}

		public VGraph<TNode, TEdge> StartTransaction()
		{
			this.Version++;
			_valueRollbacks.Push(new List<VNode<TNode, TEdge>>());
			_inEdgeRollbacks.Push(new List<VNode<TNode, TEdge>>());
			_outEdgeRollbacks.Push(new List<VNode<TNode, TEdge>>());
			return this;
		}

		public void Rollback()
		{
			foreach (var values in _valueRollbacks.Pop())
				values.RollbackValue();
			foreach (var values in _inEdgeRollbacks.Pop())
				values.RollbackIns();
			foreach (var values in _outEdgeRollbacks.Pop())
				values.RollbackOuts();
			this.Version--;
		}

		public void Dispose()
		{
			Rollback();
		}
	}

	//[DebuggerVisualizer(typeof(ExpressionBuilderVisualizer))]
	[Serializable]
	class GameState : IDisposable
	{
		VGraph<ExpressionNode, ExpressionPath> _graph = new VGraph<ExpressionNode, ExpressionPath>();
		Stack<List<VNode<ExpressionNode, ExpressionPath>>> nodes = new Stack<List<VNode<ExpressionNode, ExpressionPath>>>();
		Stack<VNode<ExpressionNode, ExpressionPath>[]> playerNodes = new Stack<VNode<ExpressionNode, ExpressionPath>[]>();
		VNode<ExpressionNode, ExpressionPath>[] playerStartNodes = new VNode<ExpressionNode, ExpressionPath>[3];
		int nodesCount = 0;

		public IEnumerable<VNode<ExpressionNode, ExpressionPath>> Nodes
		{
			get
			{
				foreach (var ns in nodes)
					foreach (var node in ns)
						yield return node;
			}
		}

		public void ClearTrace(Player[] players)
		{
			foreach (var node in Nodes)
			{
				node.WriteValue.Min = int.MinValue/2;
				node.WriteValue.Max = int.MaxValue/2;
			}

			for (int i = 0; i < 3; i++)
			{
				playerStartNodes[i].WriteValue.Min = playerStartNodes[i].WriteValue.Max = players[i].Start;
			}
		}

		public GameState(Player[] players)
		{
			nodes.Push(new List<VNode<ExpressionNode, ExpressionPath>>());

			playerNodes.Push(new VNode<ExpressionNode, ExpressionPath>[3]);
			for (int i = 0; i < 3; i++)
			{
				var node = _graph.AddNode(new ExpressionNode() { Id = i, Min = players[i].Start, Max = players[i].Start, Comment = "S("+ i.ToString() +")" });
				nodes.Peek().Add(node);
				playerStartNodes[i] = node;
				playerNodes.Peek()[i] = node;
			}
			nodesCount = 3;
		}

		public GameState StartTransaction()
		{
			_graph.StartTransaction();
			nodes.Push(new List<VNode<ExpressionNode, ExpressionPath>>());
			playerNodes.Push( (VNode<ExpressionNode, ExpressionPath>[])playerNodes.Peek().Clone());
			return this;
		}

		public void Dispose()
		{
			_graph.Rollback();

			nodesCount -= nodes.Pop().Count;
			playerNodes.Pop();
		}

		public int Max
		{
			get
			{
				int max = Int32.MinValue;
				foreach (var node in Nodes)
				{
					if (max < node.ReadValue.Max)
						max = node.ReadValue.Max;
				}

				return max;
			}
		}

		public VNode<ExpressionNode, ExpressionPath> AddPlayerNode(int i, string comment)
		{
			var nodeValue = new ExpressionNode() { Id = nodesCount++ };
			nodeValue.Comment = comment;
			var node = _graph.AddNode(nodeValue);
			nodes.Peek().Add(node);
			playerNodes.Peek()[i] = node;
			return node;
		}

		public VNode<ExpressionNode, ExpressionPath> GetPlayerNode(int i)
		{
			return playerNodes.Peek()[i];
		}

		public VNode<ExpressionNode, ExpressionPath> GetPlayerStartNode(int i)
		{
			return playerStartNodes[i];
		}

		//a <= b + c
		public bool AddMin(VNode<ExpressionNode, ExpressionPath> a, VNode<ExpressionNode, ExpressionPath> b, int c)
		{
			var path = new ExpressionPath() { Mark = c };
			_graph.AddEdge(b, a, path);

			if (a.ReadValue.Max > b.ReadValue.Max + c)
				a.WriteValue.Max = b.ReadValue.Max + c;

			if (b.ReadValue.Min < a.ReadValue.Min - c)
				b.WriteValue.Min = a.ReadValue.Min - c;

			if (!Trace(a))
				return false;
			if (!Trace(b))
				return false;
			if (!Validate())
				return false;

			return true;
		}

		bool Trace(VNode<ExpressionNode, ExpressionPath> a)
		{
			if (!TraceMin(this, a))
				return false;
			if (!TraceMax(this, a))
				return  false;
			return true;
		}

		static bool TraceMin(GameState state, VNode<ExpressionNode, ExpressionPath> x)
		{
			x.WriteValue.Traced = true;

			try
			{
				foreach (var vector in state.Maximize(x))
				{
					// x <= path.Max + path.Mark

					if (vector.Target.ReadValue.Traced)
					{
						if (vector.Target.ReadValue.Min < x.ReadValue.Min - vector.Mark)
							return false;
					}
					else
					{
						if (vector.Target.ReadValue.Min < x.ReadValue.Min - vector.Mark)
						{
							vector.Target.WriteValue.Min = x.ReadValue.Min - vector.Mark;
							if (!TraceMin(state, vector.Target))
								return false;
						}
					}
				}
				return true;
			}
			finally
			{
				x.WriteValue.Traced = false;
			}
		}

		static bool TraceMax(GameState state, VNode<ExpressionNode, ExpressionPath> x)
		{
			x.WriteValue.Traced = true;

			try
			{
				foreach (var vector in state.Minimize(x))
				{
					if (vector.Target.ReadValue.Traced)
					{
						if (vector.Target.ReadValue.Max > x.ReadValue.Max + vector.Mark)
							return false;
					}
					else
					{
						if (vector.Target.ReadValue.Max > x.ReadValue.Max + vector.Mark)
						{
							vector.Target.WriteValue.Max = x.ReadValue.Max + vector.Mark;
							if (!TraceMax(state, vector.Target))
								return false;
						}
					}
				}
				return true;
			}
			finally
			{
				x.WriteValue.Traced = false;
			}
		}

		public IEnumerable<Vector> Maximize(VNode<ExpressionNode, ExpressionPath> a)
		{
			return a.Ins.Select(path => new Vector(path.Value.Mark, path.Source));
		}

		public IEnumerable<Vector> Minimize(VNode<ExpressionNode, ExpressionPath> a)
		{
			return a.Outs.Select(path => new Vector(path.Value.Mark, path.Target));
		}

		public bool Validate()
		{
			foreach (var node in Nodes)
			{
				if (node.ReadValue.Min > node.ReadValue.Max)
					return false;
			}

			return true;
		}

		public override string ToString()
		{
			string result = "";

			foreach (var node in Nodes.Reverse())
			{
				result += node.ReadValue.ToString();
			}

			return result;
		}
	}

	[Serializable]
	struct Vector
	{
		public int Mark;
		public VNode<ExpressionNode, ExpressionPath> Target;

		public Vector(int mark, VNode<ExpressionNode, ExpressionPath> target)
		{
			Mark = mark;
			Target = target;
		}
	}

	[Serializable]
	internal class Player
	{
		public int Start;
		public int Walk;
		public int Throws;

		public bool alreadyWalked = false;
		public bool alreadyUpped = false;
		public bool alreadyThrowed = false;
		public bool Upped = false;
		public int Up = -1;
	}

	[Serializable]
	class ExpressionNode : ICloneable
	{
		public int Id;
		public int Min = int.MinValue/2;
		public int Max = int.MaxValue/2;
		public bool Traced = false;
		public string Comment;

		public object Clone()
		{
			return this.MemberwiseClone();
		}

		public override string ToString()
		{
			return Comment + String.Format("[{0}:{1}]", Min, Max);
		}

	}

	[Serializable]
	[ReadOnly(true)]
	struct ExpressionPath
	{
		public int Mark;

		public override string ToString()
		{
			var result = String.Format("({0})", Mark);
			return result;
		}
	}

	class Program
	{
		//static StreamReader inStream = new StreamReader("input.txt");
		//static string ReadLine()
		//{
		//    return inStream.ReadLine();
		//}

		static string ReadLine()
		{
			return Console.ReadLine();
		}

		static int AbsoluteMax = int.MinValue;
		static string AbsoluteMaxStr = "";

		static void Main(string[] args)
		{
			Player[] players = new Player[3];
			for (int i = 0; i < 3; i++)
			{
				players[i] = new Player();
				string[] arg = ReadLine().Split();
				players[i].Start = int.Parse(arg[0]);
				players[i].Walk = int.Parse(arg[1]);
				players[i].Throws = int.Parse(arg[2]);
			}

			var state = new GameState(players);

			Solve(state, players);
			Console.WriteLine(AbsoluteMax);
			//Console.WriteLine(AbsoluteMaxStr);
		}

		static void Solve(GameState state, Player[] players)
		{
			if (EuristicGetMax(state, players) <= AbsoluteMax)
				return;

			int max = state.Max;
			if (AbsoluteMax < max)
			{
				AbsoluteMax = max;
				AbsoluteMaxStr = state.ToString();
			}

			for (int i = 0; i < 3; i++)
			{
				using (var clone = state.StartTransaction())
				{
					DoMove(i, clone, players);
				}
			}

			for (int i = 0; i < 3; i++)
				for (int j = 0; j < 3; j++)
					if (i != j)
					{
						using (var clone = state.StartTransaction())
						{
							DoUp(i, j, clone, players);
						}
					}

			for (int i = 0; i < 3; i++)
			{
				using (var clone = state.StartTransaction())
				{
					DoThrow(i, clone, players);
				}
			}
		}

		static private void DoMove(int i, GameState state, Player[] players)
		{
			if (players[i].alreadyWalked || players[i].Up >= 0 || players[i].Upped)
				return;

			players[i].alreadyWalked = true;

			VNode<ExpressionNode, ExpressionPath> a = state.GetPlayerNode(i);
			VNode<ExpressionNode, ExpressionPath> moved = state.AddPlayerNode(i, string.Format("M({0})", i));

			if (!state.AddMin(moved, a, players[i].Walk))
				return;
			if (!state.AddMin(a, moved, players[i].Walk))
				return;

			using (var clone = state.StartTransaction())
			{
				NotEqual(players, i, clone);
			}

			players[i].alreadyWalked = false;
		}

		private static void DoThrow(int i, GameState state, Player[] players)
		{
			if (players[i].alreadyThrowed || players[i].Up < 0 || players[i].Upped)
				return;

			players[i].alreadyThrowed = true;

			int j = players[i].Up;
			players[i].Up = -1;
			players[j].Upped = false;

			VNode<ExpressionNode, ExpressionPath> a = state.GetPlayerNode(i);
			VNode<ExpressionNode, ExpressionPath> moved = state.AddPlayerNode(j, string.Format("T({0}, {1})", i, j));

			if (!state.AddMin(moved, a, players[i].Throws))
				return;
			if (!state.AddMin(a, moved, players[i].Throws))
				return;

			NotEqual(players, j, state);

			players[j].Upped = true;
			players[i].Up = j;
			players[i].alreadyThrowed = false;
		}

		private static void DoUp(int i, int j, GameState state, Player[] players)
		{
			if (players[i].alreadyUpped || players[i].Up >= 0 || players[i].Upped || players[j].Upped || i == j)
				return;

			players[i].alreadyUpped = true;
			players[i].Up = j;
			players[j].Upped = true;

			VNode<ExpressionNode, ExpressionPath> a = state.GetPlayerNode(i);
			VNode<ExpressionNode, ExpressionPath> b = state.GetPlayerNode(j);

			VNode<ExpressionNode, ExpressionPath> moved = state.AddPlayerNode(j, string.Format("U({0}, {1})", i, j));

			if (!state.AddMin(moved, a, 0))
				return;
			if (!state.AddMin(a, moved, 0))
				return;

			Equal(state, players, a, b, 1);
			Equal(state, players, a, b, -1);

			players[j].Upped = false;
			players[i].Up = -1;
			players[i].alreadyUpped = false;
		}

		//a = b + c
		private static void Equal(GameState state, Player[] players, VNode<ExpressionNode, ExpressionPath> a, VNode<ExpressionNode, ExpressionPath> b, int c)
		{
			using (var v1 = state.StartTransaction())
			{
				if (v1.AddMin(b, a, -c) && v1.AddMin(a, b, c))
					Solve(v1, players);
			}
		}

		static private void NotEqual(Player[] players, int i, GameState state)
		{
			VNode<ExpressionNode, ExpressionPath> a = state.GetPlayerNode(i); //current
			VNode<ExpressionNode, ExpressionPath> b = state.GetPlayerNode((i + 1) % 3); //not equal to
			VNode<ExpressionNode, ExpressionPath> c = state.GetPlayerNode((i + 2) % 3); //and not equal to

			using (var v1 = state.StartTransaction())
			{
				if (v1.AddMin(a, b, -1) && v1.AddMin(a, c, -1))
					Solve(v1, players);
			}

			using (var v2 = state.StartTransaction())
			{
				if (v2.AddMin(b, a, -1) && v2.AddMin(a, c, -1))
					Solve(v2, players);
			}

			using (var v3 = state.StartTransaction())
			{
				if (v3.AddMin(b, a, -1) && v3.AddMin(c, a, -1))
					Solve(v3, players);
			}

			using (var v4 = state.StartTransaction())
			{
				if (v4.AddMin(a, b, -1) && v4.AddMin(c, a, -1))
					Solve(v4, players);
			}
		}

		static int EuristicGetMax(GameState state, Player[] players)
		{
			int radius = 0;

			for (int i = 0; i < 3; i++)
			{
				if (!players[i].alreadyThrowed)
					radius += players[i].Throws;
				if (!players[i].alreadyWalked)
					radius += players[i].Walk;
				if (!players[i].alreadyUpped)
					radius += 1;
			}

			var result = Enumerable.Range(0,3).Max(i => state.GetPlayerNode(i).ReadValue.Max + radius);
			return result;
		}
	}

}