import java.util.*;
import java.math.BigInteger;

public class Euler594 {

    static final double kSqrt2 = 1.4142135623730951;

    static class Num {
        long a, b;

        Num(long a, long b) {
            this.a = a;
            this.b = b;
        }

        Num add(Num o) {
            return new Num(a + o.a, b + o.b);
        }

        Num sub(Num o) {
            return new Num(a - o.a, b - o.b);
        }

        Num mul(Num o) {
            return new Num(a * o.a + 2 * b * o.b, a * o.b + b * o.a);
        }

        boolean equals(Num o) {
            return a == o.a && b == o.b;
        }

        boolean isZero() {
            return a == 0 && b == 0;
        }

        double value() {
            return (double) a + (double) b * kSqrt2;
        }
    }

    static class Point {
        Num x, y;

        Point(Num x, Num y) {
            this.x = x;
            this.y = y;
        }

        Point add(Point o) {
            return new Point(x.add(o.x), y.add(o.y));
        }

        Point sub(Point o) {
            return new Point(x.sub(o.x), y.sub(o.y));
        }

        boolean equals(Point o) {
            return x.equals(o.x) && y.equals(o.y);
        }
    }

    static class PointKey {
        long xa, xb, ya, yb;

        PointKey(long xa, long xb, long ya, long yb) {
            this.xa = xa;
            this.xb = xb;
            this.ya = ya;
            this.yb = yb;
        }

        @Override
        public int hashCode() {
            int h = Long.hashCode(xa);
            h ^= Long.hashCode(xb) + 0x9e3779b9 + (h << 6) + (h >> 2);
            h ^= Long.hashCode(ya) + 0x9e3779b9 + (h << 6) + (h >> 2);
            h ^= Long.hashCode(yb) + 0x9e3779b9 + (h << 6) + (h >> 2);
            return h;
        }

        @Override
        public boolean equals(Object obj) {
            PointKey o = (PointKey) obj;
            return xa == o.xa && xb == o.xb && ya == o.ya && yb == o.yb;
        }
    }

    static PointKey keyOf(Point p) {
        return new PointKey(p.x.a, p.x.b, p.y.a, p.y.b);
    }

    static Point pointFromKey(PointKey k) {
        return new Point(new Num(k.xa, k.xb), new Num(k.ya, k.yb));
    }

    static class EdgeKey {
        PointKey a, b;

        EdgeKey(PointKey a, PointKey b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public int hashCode() {
            int h = a.hashCode();
            h ^= b.hashCode() + 0x9e3779b9 + (h << 6) + (h >> 2);
            return h;
        }

        @Override
        public boolean equals(Object obj) {
            EdgeKey o = (EdgeKey) obj;
            return a.equals(o.a) && b.equals(o.b);
        }
    }

    static class EdgeVal {
        Point from, to;

        EdgeVal(Point from, Point to) {
            this.from = from;
            this.to = to;
        }
    }

    static int signNum(Num v) {
        if (v.isZero())
            return 0;
        return v.value() < 0 ? -1 : 1;
    }

    static int cmpNum(Num a, Num b) {
        return signNum(a.sub(b));
    }

    static boolean pointLessGeom(Point a, Point b) {
        int cy = cmpNum(a.y, b.y);
        if (cy != 0)
            return cy < 0;
        return cmpNum(a.x, b.x) < 0;
    }

    static boolean pointkeyLess(PointKey a, PointKey b) {
        if (a.xa != b.xa)
            return a.xa < b.xa;
        if (a.xb != b.xb)
            return a.xb < b.xb;
        if (a.ya != b.ya)
            return a.ya < b.ya;
        return a.yb < b.yb;
    }

    static EdgeKey makeEdgeKey(Point a, Point b) {
        PointKey ka = keyOf(a);
        PointKey kb = keyOf(b);
        if (pointkeyLess(ka, kb))
            return new EdgeKey(ka, kb);
        return new EdgeKey(kb, ka);
    }

    static Num cross(Point a, Point b, Point c) {
        Point ab = b.sub(a);
        Point ac = c.sub(a);
        Num term1 = ab.x.mul(ac.y);
        Num term2 = ab.y.mul(ac.x);
        return term1.sub(term2);
    }

    static Num minNum(Num a, Num b) {
        return cmpNum(a, b) <= 0 ? a : b;
    }

    static Num maxNum(Num a, Num b) {
        return cmpNum(a, b) >= 0 ? a : b;
    }

    static boolean onSegment(Point a, Point b, Point p) {
        if (!cross(a, b, p).isZero())
            return false;
        if (cmpNum(p.x, minNum(a.x, b.x)) < 0 || cmpNum(p.x, maxNum(a.x, b.x)) > 0)
            return false;
        if (cmpNum(p.y, minNum(a.y, b.y)) < 0 || cmpNum(p.y, maxNum(a.y, b.y)) > 0)
            return false;
        return true;
    }

    static int orient(Point a, Point b, Point c) {
        return signNum(cross(a, b, c));
    }

    static boolean segIntersect(Point a, Point b, Point c, Point d) {
        int o1 = orient(a, b, c);
        int o2 = orient(a, b, d);
        int o3 = orient(c, d, a);
        int o4 = orient(c, d, b);
        if (o1 == 0 && onSegment(a, b, c))
            return true;
        if (o2 == 0 && onSegment(a, b, d))
            return true;
        if (o3 == 0 && onSegment(c, d, a))
            return true;
        if (o4 == 0 && onSegment(c, d, b))
            return true;
        return (o1 != o2 && o3 != o4);
    }

    static boolean pointInPolygon(Point p, List<Point> poly) {
        double px = p.x.value();
        double py = p.y.value();
        boolean inside = false;
        int n = poly.size();
        for (int i = 0; i < n; i++) {
            Point a = poly.get(i);
            Point b = poly.get((i + 1) % n);
            if (onSegment(a, b, p))
                return true;
            double ay = a.y.value();
            double by = b.y.value();
            if ((ay > py) != (by > py)) {
                double ax = a.x.value();
                double bx = b.x.value();
                double xin = ax + (bx - ax) * (py - ay) / (by - ay);
                if (xin > px)
                    inside = !inside;
            }
        }
        return inside;
    }

    static List<Point> buildVertices(List<Integer> steps, Point[] dirs) {
        List<Point> pts = new ArrayList<>();
        Point cur = new Point(new Num(0, 0), new Num(0, 0));
        pts.add(cur);
        for (int d : steps) {
            cur = cur.add(dirs[d]);
            pts.add(cur);
        }
        return pts;
    }

    static double signedArea(List<Integer> steps, Point[] dirs) {
        if (steps.isEmpty())
            return 0.0;
        List<Point> pts = buildVertices(steps, dirs);
        pts.remove(pts.size() - 1);
        double area = 0.0;
        int n = pts.size();
        for (int i = 0; i < n; i++) {
            Point a = pts.get(i);
            Point b = pts.get((i + 1) % n);
            area += a.x.value() * b.y.value() - b.x.value() * a.y.value();
        }
        return 0.5 * area;
    }

    static List<Integer> toCcw(List<Integer> steps) {
        List<Integer> out = new ArrayList<>(steps.size());
        for (int i = steps.size(); i > 0; i--) {
            out.add((steps.get(i - 1) + 4) & 7);
        }
        return out;
    }

    static Map<EdgeKey, EdgeVal> edgesFromSteps(List<Integer> steps, Point[] dirs) {
        Map<EdgeKey, EdgeVal> edges = new HashMap<>();
        Point cur = new Point(new Num(0, 0), new Num(0, 0));
        for (int d : steps) {
            Point nxt = cur.add(dirs[d]);
            edges.put(makeEdgeKey(cur, nxt), new EdgeVal(cur, nxt));
            cur = nxt;
        }
        return edges;
    }

    static class EdgeInfo {
        PointKey from, to;
        int dir;
        boolean used;

        EdgeInfo(PointKey from, PointKey to, int dir) {
            this.from = from;
            this.to = to;
            this.dir = dir;
            this.used = false;
        }
    }

    static boolean extractCyclesFromEdges(Map<EdgeKey, EdgeVal> edgeMap, Point[] dirs, List<List<Integer>> cycles) {
        cycles.clear();
        if (edgeMap.isEmpty())
            return true;

        List<EdgeInfo> edges = new ArrayList<>(edgeMap.size());
        Map<PointKey, List<Integer>> outgoing = new HashMap<>();

        for (EdgeVal e : edgeMap.values()) {
            Point diff = e.to.sub(e.from);
            int dirIdx = -1;
            for (int i = 0; i < 8; i++) {
                if (diff.equals(dirs[i])) {
                    dirIdx = i;
                    break;
                }
            }
            if (dirIdx < 0)
                return false;
            int id = edges.size();
            PointKey kFrom = keyOf(e.from);
            PointKey kTo = keyOf(e.to);
            edges.add(new EdgeInfo(kFrom, kTo, dirIdx));
            outgoing.computeIfAbsent(kFrom, k -> new ArrayList<>()).add(id);
        }

        int edgesUsed = 0;
        while (edgesUsed < edges.size()) {
            int startId = -1;
            boolean init = false;
            Point minPoint = null;
            for (int i = 0; i < edges.size(); i++) {
                if (edges.get(i).used)
                    continue;
                Point p = pointFromKey(edges.get(i).from);
                if (!init || pointLessGeom(p, minPoint)) {
                    minPoint = p;
                    startId = i;
                    init = true;
                }
            }
            if (startId < 0)
                return false;

            int curId = startId;
            List<Integer> steps = new ArrayList<>();
            while (true) {
                EdgeInfo cur = edges.get(curId);
                if (cur.used)
                    return false;
                cur.used = true;
                edgesUsed++;
                steps.add(cur.dir);

                List<Integer> outList = outgoing.get(cur.to);
                if (outList == null)
                    return false;

                int bestNext = -1;
                int bestDelta = 9;
                for (int candId : outList) {
                    if (edges.get(candId).used && candId != startId)
                        continue;
                    int delta = (edges.get(candId).dir - cur.dir + 8) & 7;
                    if (delta < bestDelta) {
                        bestDelta = delta;
                        bestNext = candId;
                    }
                }
                if (bestNext < 0)
                    return false;
                if (bestNext == startId)
                    break;
                curId = bestNext;
            }
            cycles.add(steps);
        }
        return edgesUsed == edges.size();
    }

    static String stepsKey(List<Integer> steps) {
        StringBuilder sb = new StringBuilder(steps.size());
        for (int c : steps)
            sb.append((char) c);
        return sb.toString();
    }

    static class Pair<K, V> {
        K first;
        V second;

        Pair(K first, V second) {
            this.first = first;
            this.second = second;
        }
    }

    static class Solver {
        Point[] dirs = new Point[8];
        Map<String, BigInteger> memo = new HashMap<>();

        Solver() {
            dirs[0] = new Point(new Num(2, 0), new Num(0, 0));
            dirs[1] = new Point(new Num(0, 1), new Num(0, 1));
            dirs[2] = new Point(new Num(0, 0), new Num(2, 0));
            dirs[3] = new Point(new Num(0, -1), new Num(0, 1));
            dirs[4] = new Point(new Num(-2, 0), new Num(0, 0));
            dirs[5] = new Point(new Num(0, -1), new Num(0, -1));
            dirs[6] = new Point(new Num(0, 0), new Num(-2, 0));
            dirs[7] = new Point(new Num(0, 1), new Num(0, -1));
        }

        BigInteger dfs(String key) {
            if (memo.containsKey(key))
                return memo.get(key);
            if (key.isEmpty())
                return BigInteger.ONE;

            List<Integer> steps = new ArrayList<>(key.length());
            for (int i = 0; i < key.length(); i++) {
                steps.add((int) key.charAt(i));
            }

            List<Point> pts = buildVertices(steps, dirs);
            int n = steps.size();
            int minIdx = 0;
            Point minPoint = pts.get(0);
            for (int i = 0; i < n; i++) {
                if (pointLessGeom(pts.get(i), minPoint)) {
                    minPoint = pts.get(i);
                    minIdx = i;
                }
            }

            Point p = minPoint;
            int d = steps.get(minIdx);
            List<Pair<Point, Point>> bounds = new ArrayList<>(n);
            for (int i = 0; i < n; i++) {
                bounds.add(new Pair<>(pts.get(i), pts.get(i + 1)));
            }

            BigInteger total = BigInteger.ZERO;
            Map<EdgeKey, EdgeVal> baseEdges = edgesFromSteps(steps, dirs);

            for (int delta = 1; delta <= 3; delta++) {
                int vd = (d + delta) & 7;
                Point u = dirs[d];
                Point v = dirs[vd];
                Point p1 = p;
                Point p2 = p.add(u);
                Point p3 = p2.add(v);
                Point p4 = p.add(v);

                if (!pointInPolygon(p4, pts) || !pointInPolygon(p3, pts))
                    continue;

                boolean ok = true;
                Pair<Point, Point>[] tileEdges = new Pair[] {
                        new Pair<>(p1, p2), new Pair<>(p2, p3), new Pair<>(p3, p4), new Pair<>(p4, p1)
                };

                for (Pair<Point, Point> te : tileEdges) {
                    for (Pair<Point, Point> be : bounds) {
                        Point a = te.first;
                        Point bX = te.second;
                        Point c = be.first;
                        Point d2 = be.second;
                        if ((a.equals(c) && bX.equals(d2)) || (a.equals(d2) && bX.equals(c)))
                            continue;
                        if (a.equals(c) || a.equals(d2) || bX.equals(c) || bX.equals(d2))
                            continue;
                        if (segIntersect(a, bX, c, d2)) {
                            ok = false;
                            break;
                        }
                    }
                    if (!ok)
                        break;
                }
                if (!ok)
                    continue;

                Map<EdgeKey, EdgeVal> edges = new HashMap<>(baseEdges);
                Pair<Point, Point>[] cwEdges = new Pair[] {
                        new Pair<>(p1, p4), new Pair<>(p4, p3), new Pair<>(p3, p2), new Pair<>(p2, p1)
                };

                for (Pair<Point, Point> e : cwEdges) {
                    EdgeKey ek = makeEdgeKey(e.first, e.second);
                    if (edges.containsKey(ek)) {
                        edges.remove(ek);
                    } else {
                        edges.put(ek, new EdgeVal(e.first, e.second));
                    }
                }

                List<List<Integer>> cycles = new ArrayList<>();
                if (!extractCyclesFromEdges(edges, dirs, cycles))
                    continue;
                if (cycles.isEmpty()) {
                    total = total.add(BigInteger.ONE);
                    continue;
                }

                BigInteger ways = BigInteger.ONE;
                for (List<Integer> cycle : cycles) {
                    if (signedArea(cycle, dirs) < 0)
                        cycle = toCcw(cycle);
                    ways = ways.multiply(dfs(stepsKey(cycle)));
                }
                total = total.add(ways);
            }

            memo.put(key, total);
            return total;
        }

        BigInteger solve(int a, int b) {
            List<Integer> steps = new ArrayList<>(8 * (a + b));
            int[] lens = { a, b, a, b, a, b, a, b };
            for (int i = 0; i < 8; i++) {
                for (int j = 0; j < lens[i]; j++) {
                    steps.add(i);
                }
            }
            return dfs(stepsKey(steps));
        }
    }

    public static String solve() {
        Solver s = new Solver();
        return s.solve(4, 2).toString();
    }

    public static void main(String[] args) {
        System.out.println(solve());
    }
}
