import java.util.*;
import java.util.concurrent.*;

public class Euler252 {
    static final int TARGET_POINT_COUNT = 500;
    static final long MOD = 50515093L;

    static class Point {
        int x, y;

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

    static long cross(Point a, Point b, Point c) {
        return (long) (b.x - a.x) * (c.y - a.y) - (long) (b.y - a.y) * (c.x - a.x);
    }

    static long dist2(Point a, Point b) {
        long dx = (long) a.x - b.x;
        long dy = (long) a.y - b.y;
        return dx * dx + dy * dy;
    }

    static List<Point> generatePoints(int count) {
        long s = 290797;
        List<Integer> tValues = new ArrayList<>(count * 2);
        for (int i = 0; i < count * 2; ++i) {
            s = (s * s) % MOD;
            tValues.add((int) (s % 2000) - 1000);
        }
        List<Point> points = new ArrayList<>(count);
        for (int i = 0; i < count; ++i) {
            points.add(new Point(tValues.get(2 * i), tValues.get(2 * i + 1)));
        }
        return points;
    }

    static boolean pointOnSegmentInclusive(Point a, Point b, Point p) {
        if (cross(a, b, p) != 0)
            return false;
        int minX = Math.min(a.x, b.x);
        int maxX = Math.max(a.x, b.x);
        int minY = Math.min(a.y, b.y);
        int maxY = Math.max(a.y, b.y);
        return p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY;
    }

    static class GeometryCache {
        int n;
        long[][] leftBits;
        boolean[][] hasInner;

        GeometryCache(int n) {
            this.n = n;
            int words = (n + 63) / 64;
            leftBits = new long[n * n][words];
            hasInner = new boolean[n][n];
        }
    }

    static GeometryCache precomputeGeometry(List<Point> points) {
        int n = points.size();
        GeometryCache cache = new GeometryCache(n);

        int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
        ExecutorService executor = Executors.newFixedThreadPool(threads);
        List<Future<?>> futures = new ArrayList<>();

        for (int a = 0; a < n; ++a) {
            final int fa = a;
            futures.add(executor.submit(() -> {
                for (int b = 0; b < n; ++b) {
                    if (fa == b)
                        continue;

                    long[] bits = cache.leftBits[fa * n + b];
                    boolean inner = false;

                    for (int p = 0; p < n; ++p) {
                        if (p == fa || p == b)
                            continue;
                        long cr = cross(points.get(fa), points.get(b), points.get(p));
                        if (cr > 0) {
                            bits[p >> 6] |= (1L << (p & 63));
                        } else if (!inner && cr == 0
                                && pointOnSegmentInclusive(points.get(fa), points.get(b), points.get(p))) {
                            inner = true;
                        }
                    }
                    cache.hasInner[fa][b] = inner;
                }
            }));
        }

        for (Future<?> f : futures) {
            try {
                f.get();
            } catch (Exception e) {
            }
        }
        executor.shutdown();
        return cache;
    }

    static boolean triangleIsEmptyStrict(int a, int b, int c, GeometryCache cache) {
        long[] ab = cache.leftBits[a * cache.n + b];
        long[] bc = cache.leftBits[b * cache.n + c];
        long[] ca = cache.leftBits[c * cache.n + a];

        for (int w = 0; w < ab.length; ++w) {
            if ((ab[w] & bc[w] & ca[w]) != 0)
                return false;
        }
        return true;
    }

    static long solveForAnchor(int anchor, List<Point> points, GeometryCache cache) {
        int n = points.size();
        List<Integer> verts = new ArrayList<>();

        for (int idx = 0; idx < n; ++idx) {
            if (idx == anchor)
                continue;
            if (points.get(idx).y > points.get(anchor).y ||
                    (points.get(idx).y == points.get(anchor).y && points.get(idx).x > points.get(anchor).x)) {
                verts.add(idx);
            }
        }

        int m = verts.size();
        if (m < 2)
            return 0;

        verts.sort((lhs, rhs) -> {
            long cr = cross(points.get(anchor), points.get(lhs), points.get(rhs));
            if (cr != 0)
                return cr > 0 ? -1 : 1;
            long dLhs = dist2(points.get(anchor), points.get(lhs));
            long dRhs = dist2(points.get(anchor), points.get(rhs));
            if (dLhs != dRhs)
                return dLhs < dRhs ? -1 : 1;
            return Integer.compare(lhs, rhs);
        });

        long[] dp = new long[m * m];
        Arrays.fill(dp, -1);
        List<List<Integer>> incoming = new ArrayList<>(m);
        for (int i = 0; i < m; i++)
            incoming.add(new ArrayList<>());

        long best = 0;

        for (int j = 0; j < m - 1; ++j) {
            int vj = verts.get(j);
            boolean canExtendOverJ = !cache.hasInner[anchor][vj];

            for (int k = j + 1; k < m; ++k) {
                int vk = verts.get(k);
                long triArea2 = cross(points.get(anchor), points.get(vj), points.get(vk));
                if (triArea2 <= 0)
                    continue;

                if (!triangleIsEmptyStrict(anchor, vj, vk, cache))
                    continue;

                long current = triArea2;

                if (canExtendOverJ) {
                    for (int h : incoming.get(j)) {
                        int vh = verts.get(h);
                        if (cross(points.get(vh), points.get(vj), points.get(vk)) <= 0)
                            continue;
                        long candidate = dp[h * m + j] + triArea2;
                        if (candidate > current) {
                            current = candidate;
                        }
                    }
                }

                dp[j * m + k] = current;
                incoming.get(k).add(j);
                if (current > best)
                    best = current;
            }
        }
        return best;
    }

    public static String solve() {
        List<Point> points = generatePoints(TARGET_POINT_COUNT);
        GeometryCache cache = precomputeGeometry(points);

        int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
        ExecutorService executor = Executors.newFixedThreadPool(threads);
        List<Future<Long>> futures = new ArrayList<>();

        for (int anchor = 0; anchor < points.size(); ++anchor) {
            final int fAnchor = anchor;
            futures.add(executor.submit(() -> solveForAnchor(fAnchor, points, cache)));
        }

        long best = 0;
        for (Future<Long> f : futures) {
            try {
                long val = f.get();
                if (val > best)
                    best = val;
            } catch (Exception e) {
            }
        }
        executor.shutdown();

        double ans = best / 2.0;
        return String.format(Locale.US, "%.1f", ans);
    }

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