#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""HTTP utility code shared by clients and servers.

This module also defines the `HTTPServerRequest` class which is exposed
via `tornado.web.RequestHandler.request`.
"""

import calendar
import collections
import copy
import datetime
import email.utils
from http.client import responses
import http.cookies
import re
from ssl import SSLError
import time
import unicodedata
from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl

from tornado.escape import native_str, parse_qs_bytes, utf8
from tornado.log import gen_log
from tornado.util import ObjectDict, unicode_type

# responses is unused in this file, but we re-export it to other files.
# Reference it so pyflakes doesn't complain.
responses

import typing
from typing import (
    Tuple,
    Iterable,
    List,
    Mapping,
    Iterator,
    Dict,
    Union,
    Optional,
    Awaitable,
    Generator,
    AnyStr,
)

if typing.TYPE_CHECKING:
    from typing import Deque  # noqa: F401
    from asyncio import Future  # noqa: F401
    import unittest  # noqa: F401

# RFC 7230 section 3.5: a recipient MAY recognize a single LF as a line
# terminator and ignore any preceding CR.
_CRLF_RE = re.compile(r"\r?\n")

class _NormalizedHeaderCache(dict):
    """Dynamic cached mapping of header names to Http-Header-Case.

    Implemented as a dict subclass so that cache hits are as fast as a
    normal dict lookup, without the overhead of a python function
    call.

    >>> normalized_headers = _NormalizedHeaderCache(10)
    >>> normalized_headers["coNtent-TYPE"]
    'Content-Type'
    """

    def __init__(self, size: int) -> None:
        super(_NormalizedHeaderCache, self).__init__()
        self.size = size
        self.queue = collections.deque()  # type: Deque[str]

    def __missing__(self, key: str) -> str:
        normalized = "-".join([w.capitalize() for w in key.split("-")])
        self[key] = normalized
        self.queue.append(key)
        if len(self.queue) > self.size:
            # Limit the size of the cache.  LRU would be better, but this
            # simpler approach should be fine.  In Python 2.7+ we could
            # use OrderedDict (or in 3.2+, @functools.lru_cache).
            old_key = self.queue.popleft()
            del self[old_key]
        return normalized

_normalized_headers = _NormalizedHeaderCache(1000)

class HTTPHeaders(collections.abc.MutableMapping):
    """A dictionary that maintains ``Http-Header-Case`` for all keys.

    Supports multiple values per key via a pair of new methods,
    `add()` and `get_list()`.  The regular dictionary interface
    returns a single value per key, with multiple values joined by a
    comma.

    >>> h = HTTPHeaders({"content-type": "text/html"})
    >>> list(h.keys())
    ['Content-Type']
    >>> h["Content-Type"]
    'text/html'

    >>> h.add("Set-Cookie", "A=B")
    >>> h.add("Set-Cookie", "C=D")
    >>> h["set-cookie"]
    'A=B,C=D'
    >>> h.get_list("set-cookie")
    ['A=B', 'C=D']

    >>> for (k,v) in sorted(h.get_all()):
    ...    print('%s: %s' % (k,v))
    ...
    Content-Type: text/html
    Set-Cookie: A=B
    Set-Cookie: C=D
    """

    @typing.overload
    def __init__(self, __arg: Mapping[str, List[str]]) -> None:
        pass

    @typing.overload  # noqa: F811
    def __init__(self, __arg: Mapping[str, str]) -> None:
        pass

    @typing.overload  # noqa: F811
    def __init__(self, *args: Tuple[str, str]) -> None:
        pass

    @typing.overload  # noqa: F811
    def __init__(self, **kwargs: str) -> None:
        pass

    def __init__(self, *args: typing.Any, **kwargs: str) -> None:  # noqa: F811
        self._dict = {}  # type: typing.Dict[str, str]
        self._as_list = {}  # type: typing.Dict[str, typing.List[str]]
        self._last_key = None
        if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], HTTPHeaders):
            # Copy constructor
            for k, v in args[0].get_all():
                self.add(k, v)
        else:
            # Dict-style initialization
            self.update(*args, **kwargs)

    # new public methods

    def add(self, name: str, value: str) -> None:
        """Adds a new value for the given key."""
        norm_name = _normalized_headers[name]
        self._last_key = norm_name
        if norm_name in self:
            self._dict[norm_name] = (
                native_str(self[norm_name]) + "," + native_str(value)
            )
            self._as_list[norm_name].append(value)
        else:
            self[norm_name] = value

    def get_list(self, name: str) -> List[str]:
        """Returns all values for the given header as a list."""
        norm_name = _normalized_headers[name]
        return self._as_list.get(norm_name, [])

    def get_all(self) -> Iterable[Tuple[str, str]]:
        """Returns an iterable of all (name, value) pairs.

        If a header has multiple values, multiple pairs will be
        returned with the same name.
        """
        for name, values in self._as_list.items():
            for value in values:
                yield (name, value)

    def parse_line(self, line: str) -> None:
        """Updates the dictionary with a single header line.

        >>> h = HTTPHeaders()
        >>> h.parse_line("Content-Type: text/html")
        >>> h.get('content-type')
        'text/html'
        """
        if line[0].isspace():
            # continuation of a multi-line header
            if self._last_key is None:
                raise HTTPInputError("first header line cannot start with whitespace")
            new_part = " " + line.lstrip()
            self._as_list[self._last_key][-1] += new_part
            self._dict[self._last_key] += new_part
        else:
            try:
                name, value = line.split(":", 1)
            except ValueError:
                raise HTTPInputError("no colon in header line")
            self.add(name, value.strip())

    @classmethod
    def parse(cls, headers: str) -> "HTTPHeaders":
        """Returns a dictionary from HTTP header text.

        >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n")
        >>> sorted(h.items())
        [('Content-Length', '42'), ('Content-Type', 'text/html')]

        .. versionchanged:: 5.1

           Raises `HTTPInputError` on malformed headers instead of a
           mix of `KeyError`, and `ValueError`.

        """
        h = cls()
        for line in _CRLF_RE.split(headers):
            if line:
                h.parse_line(line)
        return h

    # MutableMapping abstract method implementations.

    def __setitem__(self, name: str, value: str) -> None:
        norm_name = _normalized_headers[name]
        self._dict[norm_name] = value
        self._as_list[norm_name] = [value]

    def __getitem__(self, name: str) -> str:
        return self._dict[_normalized_headers[name]]

    def __delitem__(self, name: str) -> None:
        norm_name = _normalized_headers[name]
        del self._dict[norm_name]
        del self._as_list[norm_name]

    def __len__(self) -> int:
        return len(self._dict)

    def __iter__(self) -> Iterator[typing.Any]:
        return iter(self._dict)

    def copy(self) -> "HTTPHeaders":
        # defined in dict but not in MutableMapping.
        return HTTPHeaders(self)

    # Use our overridden copy method for the copy.copy module.
    # This makes shallow copies one level deeper, but preserves
    # the appearance that HTTPHeaders is a single container.
    __copy__ = copy

    def __str__(self) -> str:
        lines = []
        for name, value in self.get_all():
            lines.append("%s: %s\n" % (name, value))
        return "".join(lines)

    __unicode__ = __str__

class HTTPServerRequest(object):
    """A single HTTP request.

    All attributes are type `str` unless otherwise noted.

    .. attribute:: method

       HTTP request method, e.g. "GET" or "POST"

    .. attribute:: uri

       The requested uri.

    .. attribute:: path

       The path portion of `uri`

    .. attribute:: query

       The query portion of `uri`

    .. attribute:: version

       HTTP version specified in request, e.g. "HTTP/1.1"

    .. attribute:: headers

       `.HTTPHeaders` dictionary-like object for request headers.  Acts like
       a case-insensitive dictionary with additional methods for repeated
       headers.

    .. attribute:: body

       Request body, if present, as a byte string.

    .. attribute:: remote_ip

       Client's IP address as a string.  If ``HTTPServer.xheaders`` is set,
       will pass along the real IP address provided by a load balancer
       in the ``X-Real-Ip`` or ``X-Forwarded-For`` header.

    .. versionchanged:: 3.1
       The list format of ``X-Forwarded-For`` is now supported.

    .. attribute:: protocol

       The protocol used, either "http" or "https".  If ``HTTPServer.xheaders``
       is set, will pass along the protocol used by a load balancer if
       reported via an ``X-Scheme`` header.

    .. attribute:: host

       The requested hostname, usually taken from the ``Host`` header.

    .. attribute:: arguments

       GET/POST arguments are available in the arguments property, which
       maps arguments names to lists of values (to support multiple values
       for individual names). Names are of type `str`, while arguments
       are byte strings.  Note that this is different from
       `.RequestHandler.get_argument`, which returns argument values as
       unicode strings.

    .. attribute:: query_arguments

       Same format as ``arguments``, but contains only arguments extracted
       from the query string.

       .. versionadded:: 3.2

    .. attribute:: body_arguments

       Same format as ``arguments``, but contains only arguments extracted
       from the request body.

       .. versionadded:: 3.2

    .. attribute:: files

       File uploads are available in the files property, which maps file
       names to lists of `.HTTPFile`.

    .. attribute:: connection

       An HTTP request is attached to a single HTTP connection, which can
       be accessed through the "connection" attribute. Since connections
       are typically kept open in HTTP/1.1, multiple requests can be handled
       sequentially on a single connection.

    .. versionchanged:: 4.0
       Moved from ``tornado.httpserver.HTTPRequest``.
    """

    path = None  # type: str
    query = None  # type: str

    # HACK: Used for stream_request_body
    _body_future = None  # type: Future[None]

    def __init__(
        self,
        method: str = None,
        uri: str = None,
        version: str = "HTTP/1.0",
        headers: HTTPHeaders = None,
        body: bytes = None,
        host: str = None,
        files: Dict[str, List["HTTPFile"]] = None,
        connection: "HTTPConnection" = None,
        start_line: "RequestStartLine" = None,
        server_connection: object = None,
    ) -> None:
        if start_line is not None:
            method, uri, version = start_line
        self.method = method
        self.uri = uri
        self.version = version
        self.headers = headers or HTTPHeaders()
        self.body = body or b""

        # set remote IP and protocol
        context = getattr(connection, "context", None)
        self.remote_ip = getattr(context, "remote_ip", None)
        self.protocol = getattr(context, "protocol", "http")

        self.host = host or self.headers.get("Host") or "127.0.0.1"
        self.host_name = split_host_and_port(self.host.lower())[0]
        self.files = files or {}
        self.connection = connection
        self.server_connection = server_connection
        self._start_time = time.time()
        self._finish_time = None

        if uri is not None:
            self.path, sep, self.query = uri.partition("?")
        self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
        self.query_arguments = copy.deepcopy(self.arguments)
        self.body_arguments = {}  # type: Dict[str, List[bytes]]

    @property
    def cookies(self) -> Dict[str, http.cookies.Morsel]:
        """A dictionary of ``http.cookies.Morsel`` objects."""
        if not hasattr(self, "_cookies"):
            self._cookies = http.cookies.SimpleCookie()
            if "Cookie" in self.headers:
                try:
                    parsed = parse_cookie(self.headers["Cookie"])
                except Exception:
                    pass
                else:
                    for k, v in parsed.items():
                        try:
                            self._cookies[k] = v
                        except Exception:
                            # SimpleCookie imposes some restrictions on keys;
                            # parse_cookie does not. Discard any cookies
                            # with disallowed keys.
                            pass
        return self._cookies

    def full_url(self) -> str:
        """Reconstructs the full URL for this request."""
        return self.protocol + "://" + self.host + self.uri

    def request_time(self) -> float:
        """Returns the amount of time it took for this request to execute."""
        if self._finish_time is None:
            return time.time() - self._start_time
        else:
            return self._finish_time - self._start_time

    def get_ssl_certificate(
        self, binary_form: bool = False
    ) -> Union[None, Dict, bytes]:
        """Returns the client's SSL certificate, if any.

        To use client certificates, the HTTPServer's
        `ssl.SSLContext.verify_mode` field must be set, e.g.::

            ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            ssl_ctx.load_cert_chain("foo.crt", "foo.key")
            ssl_ctx.load_verify_locations("cacerts.pem")
            ssl_ctx.verify_mode = ssl.CERT_REQUIRED
            server = HTTPServer(app, ssl_options=ssl_ctx)

        By default, the return value is a dictionary (or None, if no
        client certificate is present).  If ``binary_form`` is true, a
        DER-encoded form of the certificate is returned instead.  See
        SSLSocket.getpeercert() in the standard library for more
        details.
        http://docs.python.org/library/ssl.html#sslsocket-objects
        """
        try:
            if self.connection is None:
                return None
            # TODO: add a method to HTTPConnection for this so it can work with HTTP/2
            return self.connection.stream.socket.getpeercert(  # type: ignore
                binary_form=binary_form
            )
        except SSLError:
            return None

    def _parse_body(self) -> None:
        parse_body_arguments(
            self.headers.get("Content-Type", ""),
            self.body,
            self.body_arguments,
            self.files,
            self.headers,
        )

        for k, v in self.body_arguments.items():
            self.arguments.setdefault(k, []).extend(v)

    def __repr__(self) -> str:
        attrs = ("protocol", "host", "method", "uri", "version", "remote_ip")
        args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
        return "%s(%s)" % (self.__class__.__name__, args)

class HTTPInputError(Exception):
    """Exception class for malformed HTTP requests or responses
    from remote sources.

    .. versionadded:: 4.0
    """

    pass

class HTTPOutputError(Exception):
    """Exception class for errors in HTTP output.

    .. versionadded:: 4.0
    """

    pass

class HTTPServerConnectionDelegate(object):
    """Implement this interface to handle requests from `.HTTPServer`.

    .. versionadded:: 4.0
    """

    def start_request(
        self, server_conn: object, request_conn: "HTTPConnection"
    ) -> "HTTPMessageDelegate":
        """This method is called by the server when a new request has started.

        :arg server_conn: is an opaque object representing the long-lived
            (e.g. tcp-level) connection.
        :arg request_conn: is a `.HTTPConnection` object for a single
            request/response exchange.

        This method should return a `.HTTPMessageDelegate`.
        """
        raise NotImplementedError()

    def on_close(self, server_conn: object) -> None:
        """This method is called when a connection has been closed.

        :arg server_conn: is a server connection that has previously been
            passed to ``start_request``.
        """
        pass

class HTTPMessageDelegate(object):
    """Implement this interface to handle an HTTP request or response.

    .. versionadded:: 4.0
    """

    # TODO: genericize this class to avoid exposing the Union.
    def headers_received(
        self,
        start_line: Union["RequestStartLine", "ResponseStartLine"],
        headers: HTTPHeaders,
    ) -> Optional[Awaitable[None]]:
        """Called when the HTTP headers have been received and parsed.

        :arg start_line: a `.RequestStartLine` or `.ResponseStartLine`
            depending on whether this is a client or server message.
        :arg headers: a `.HTTPHeaders` instance.

        Some `.HTTPConnection` methods can only be called during
        ``headers_received``.

        May return a `.Future`; if it does the body will not be read
        until it is done.
        """
        pass

    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
        """Called when a chunk of data has been received.

        May return a `.Future` for flow control.
        """
        pass

    def finish(self) -> None:
        """Called after the last chunk of data has been received."""
        pass

    def on_connection_close(self) -> None:
        """Called if the connection is closed without finishing the request.

        If ``headers_received`` is called, either ``finish`` or
        ``on_connection_close`` will be called, but not both.
        """
        pass

class HTTPConnection(object):
    """Applications use this interface to write their responses.

    .. versionadded:: 4.0
    """

    def write_headers(
        self,
        start_line: Union["RequestStartLine", "ResponseStartLine"],
        headers: HTTPHeaders,
        chunk: bytes = None,
    ) -> "Future[None]":
        """Write an HTTP header block.

        :arg start_line: a `.RequestStartLine` or `.ResponseStartLine`.
        :arg headers: a `.HTTPHeaders` instance.
        :arg chunk: the first (optional) chunk of data.  This is an optimization
            so that small responses can be written in the same call as their
            headers.

        The ``version`` field of ``start_line`` is ignored.

        Returns a future for flow control.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed.
        """
        raise NotImplementedError()

    def write(self, chunk: bytes) -> "Future[None]":
        """Writes a chunk of body data.

        Returns a future for flow control.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed.
        """
        raise NotImplementedError()

    def finish(self) -> None:
        """Indicates that the last body data has been written.
        """
        raise NotImplementedError()

def url_concat(
    url: str,
    args: Union[
        None, Dict[str, str], List[Tuple[str, str]], Tuple[Tuple[str, str], ...]
    ],
) -> str:
    """Concatenate url and arguments regardless of whether
    url has existing query parameters.

    ``args`` may be either a dictionary or a list of key-value pairs
    (the latter allows for multiple values with the same key.

    >>> url_concat("http://example.com/foo", dict(c="d"))
    'http://example.com/foo?c=d'
    >>> url_concat("http://example.com/foo?a=b", dict(c="d"))
    'http://example.com/foo?a=b&c=d'
    >>> url_concat("http://example.com/foo?a=b", [("c", "d"), ("c", "d2")])
    'http://example.com/foo?a=b&c=d&c=d2'
    """
    if args is None:
        return url
    parsed_url = urlparse(url)
    if isinstance(args, dict):
        parsed_query = parse_qsl(parsed_url.query, keep_blank_values=True)
        parsed_query.extend(args.items())
    elif isinstance(args, list) or isinstance(args, tuple):
        parsed_query = parse_qsl(parsed_url.query, keep_blank_values=True)
        parsed_query.extend(args)
    else:
        err = "'args' parameter should be dict, list or tuple. Not {0}".format(
            type(args)
        )
        raise TypeError(err)
    final_query = urlencode(parsed_query)
    url = urlunparse(
        (
            parsed_url[0],
            parsed_url[1],
            parsed_url[2],
            parsed_url[3],
            final_query,
            parsed_url[5],
        )
    )
    return url

class HTTPFile(ObjectDict):
    """Represents a file uploaded via a form.

    For backwards compatibility, its instance attributes are also
    accessible as dictionary keys.

    * ``filename``
    * ``body``
    * ``content_type``
    """

    pass

def _parse_request_range(
    range_header: str
) -> Optional[Tuple[Optional[int], Optional[int]]]:
    """Parses a Range header.

    Returns either ``None`` or tuple ``(start, end)``.
    Note that while the HTTP headers use inclusive byte positions,
    this method returns indexes suitable for use in slices.

    >>> start, end = _parse_request_range("bytes=1-2")
    >>> start, end
    (1, 3)
    >>> [0, 1, 2, 3, 4][start:end]
    [1, 2]
    >>> _parse_request_range("bytes=6-")
    (6, None)
    >>> _parse_request_range("bytes=-6")
    (-6, None)
    >>> _parse_request_range("bytes=-0")
    (None, 0)
    >>> _parse_request_range("bytes=")
    (None, None)
    >>> _parse_request_range("foo=42")
    >>> _parse_request_range("bytes=1-2,6-10")

    Note: only supports one range (ex, ``bytes=1-2,6-10`` is not allowed).

    See [0] for the details of the range header.

    [0]: http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#byte.ranges
    """
    unit, _, value = range_header.partition("=")
    unit, value = unit.strip(), value.strip()
    if unit != "bytes":
        return None
    start_b, _, end_b = value.partition("-")
    try:
        start = _int_or_none(start_b)
        end = _int_or_none(end_b)
    except ValueError:
        return None
    if end is not None:
        if start is None:
            if end != 0:
                start = -end
                end = None
        else:
            end += 1
    return (start, end)

def _get_content_range(start: Optional[int], end: Optional[int], total: int) -> str:
    """Returns a suitable Content-Range header:

    >>> print(_get_content_range(None, 1, 4))
    bytes 0-0/4
    >>> print(_get_content_range(1, 3, 4))
    bytes 1-2/4
    >>> print(_get_content_range(None, None, 4))
    bytes 0-3/4
    """
    start = start or 0
    end = (end or total) - 1
    return "bytes %s-%s/%s" % (start, end, total)

def _int_or_none(val: str) -> Optional[int]:
    val = val.strip()
    if val == "":
        return None
    return int(val)

def parse_body_arguments(
    content_type: str,
    body: bytes,
    arguments: Dict[str, List[bytes]],
    files: Dict[str, List[HTTPFile]],
    headers: HTTPHeaders = None,
) -> None:
    """Parses a form request body.

    Supports ``application/x-www-form-urlencoded`` and
    ``multipart/form-data``.  The ``content_type`` parameter should be
    a string and ``body`` should be a byte string.  The ``arguments``
    and ``files`` parameters are dictionaries that will be updated
    with the parsed contents.
    """
    if content_type.startswith("application/x-www-form-urlencoded"):
        if headers and "Content-Encoding" in headers:
            gen_log.warning(
                "Unsupported Content-Encoding: %s", headers["Content-Encoding"]
            )
            return
        try:
            uri_arguments = parse_qs_bytes(native_str(body), keep_blank_values=True)
        except Exception as e:
            gen_log.warning("Invalid x-www-form-urlencoded body: %s", e)
            uri_arguments = {}
        for name, values in uri_arguments.items():
            if values:
                arguments.setdefault(name, []).extend(values)
    elif content_type.startswith("multipart/form-data"):
        if headers and "Content-Encoding" in headers:
            gen_log.warning(
                "Unsupported Content-Encoding: %s", headers["Content-Encoding"]
            )
            return
        try:
            fields = content_type.split(";")
            for field in fields:
                k, sep, v = field.strip().partition("=")
                if k == "boundary" and v:
                    parse_multipart_form_data(utf8(v), body, arguments, files)
                    break
            else:
                raise ValueError("multipart boundary not found")
        except Exception as e:
            gen_log.warning("Invalid multipart/form-data: %s", e)

def parse_multipart_form_data(
    boundary: bytes,
    data: bytes,
    arguments: Dict[str, List[bytes]],
    files: Dict[str, List[HTTPFile]],
) -> None:
    """Parses a ``multipart/form-data`` body.

    The ``boundary`` and ``data`` parameters are both byte strings.
    The dictionaries given in the arguments and files parameters
    will be updated with the contents of the body.

    .. versionchanged:: 5.1

       Now recognizes non-ASCII filenames in RFC 2231/5987
       (``filename*=``) format.
    """
    # The standard allows for the boundary to be quoted in the header,
    # although it's rare (it happens at least for google app engine
    # xmpp).  I think we're also supposed to handle backslash-escapes
    # here but I'll save that until we see a client that uses them
    # in the wild.
    if boundary.startswith(b'"') and boundary.endswith(b'"'):
        boundary = boundary[1:-1]
    final_boundary_index = data.rfind(b"--" + boundary + b"--")
    if final_boundary_index == -1:
        gen_log.warning("Invalid multipart/form-data: no final boundary")
        return
    parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n")
    for part in parts:
        if not part:
            continue
        eoh = part.find(b"\r\n\r\n")
        if eoh == -1:
            gen_log.warning("multipart/form-data missing headers")
            continue
        headers = HTTPHeaders.parse(part[:eoh].decode("utf-8"))
        disp_header = headers.get("Content-Disposition", "")
        disposition, disp_params = _parse_header(disp_header)
        if disposition != "form-data" or not part.endswith(b"\r\n"):
            gen_log.warning("Invalid multipart/form-data")
            continue
        value = part[eoh + 4 : -2]
        if not disp_params.get("name"):
            gen_log.warning("multipart/form-data value missing name")
            continue
        name = disp_params["name"]
        if disp_params.get("filename"):
            ctype = headers.get("Content-Type", "application/unknown")
            files.setdefault(name, []).append(
                HTTPFile(
                    filename=disp_params["filename"], body=value, content_type=ctype
                )
            )
        else:
            arguments.setdefault(name, []).append(value)

def format_timestamp(
    ts: Union[int, float, tuple, time.struct_time, datetime.datetime]
) -> str:
    """Formats a timestamp in the format used by HTTP.

    The argument may be a numeric timestamp as returned by `time.time`,
    a time tuple as returned by `time.gmtime`, or a `datetime.datetime`
    object.

    >>> format_timestamp(1359312200)
    'Sun, 27 Jan 2013 18:43:20 GMT'
    """
    if isinstance(ts, (int, float)):
        time_num = ts
    elif isinstance(ts, (tuple, time.struct_time)):
        time_num = calendar.timegm(ts)
    elif isinstance(ts, datetime.datetime):
        time_num = calendar.timegm(ts.utctimetuple())
    else:
        raise TypeError("unknown timestamp type: %r" % ts)
    return email.utils.formatdate(time_num, usegmt=True)

RequestStartLine = collections.namedtuple(
    "RequestStartLine", ["method", "path", "version"]
)

def parse_request_start_line(line: str) -> RequestStartLine:
    """Returns a (method, path, version) tuple for an HTTP 1.x request line.

    The response is a `collections.namedtuple`.

    >>> parse_request_start_line("GET /foo HTTP/1.1")
    RequestStartLine(method='GET', path='/foo', version='HTTP/1.1')
    """
    try:
        method, path, version = line.split(" ")
    except ValueError:
        # https://tools.ietf.org/html/rfc7230#section-3.1.1
        # invalid request-line SHOULD respond with a 400 (Bad Request)
        raise HTTPInputError("Malformed HTTP request line")
    if not re.match(r"^HTTP/1\.[0-9]$", version):
        raise HTTPInputError(
            "Malformed HTTP version in HTTP Request-Line: %r" % version
        )
    return RequestStartLine(method, path, version)

ResponseStartLine = collections.namedtuple(
    "ResponseStartLine", ["version", "code", "reason"]
)

def parse_response_start_line(line: str) -> ResponseStartLine:
    """Returns a (version, code, reason) tuple for an HTTP 1.x response line.

    The response is a `collections.namedtuple`.

    >>> parse_response_start_line("HTTP/1.1 200 OK")
    ResponseStartLine(version='HTTP/1.1', code=200, reason='OK')
    """
    line = native_str(line)
    match = re.match("(HTTP/1.[0-9]) ([0-9]+) ([^\r]*)", line)
    if not match:
        raise HTTPInputError("Error parsing response start line")
    return ResponseStartLine(match.group(1), int(match.group(2)), match.group(3))

# _parseparam and _parse_header are copied and modified from python2.7's cgi.py
# The original 2.7 version of this code did not correctly support some
# combinations of semicolons and double quotes.
# It has also been modified to support valueless parameters as seen in
# websocket extension negotiations, and to support non-ascii values in
# RFC 2231/5987 format.

def _parseparam(s: str) -> Generator[str, None, None]:
    while s[:1] == ";":
        s = s[1:]
        end = s.find(";")
        while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
            end = s.find(";", end + 1)
        if end < 0:
            end = len(s)
        f = s[:end]
        yield f.strip()
        s = s[end:]

def _parse_header(line: str) -> Tuple[str, Dict[str, str]]:
    r"""Parse a Content-type like header.

    Return the main content-type and a dictionary of options.

    >>> d = "form-data; foo=\"b\\\\a\\\"r\"; file*=utf-8''T%C3%A4st"
    >>> ct, d = _parse_header(d)
    >>> ct
    'form-data'
    >>> d['file'] == r'T\u00e4st'.encode('ascii').decode('unicode_escape')
    True
    >>> d['foo']
    'b\\a"r'
    """
    parts = _parseparam(";" + line)
    key = next(parts)
    # decode_params treats first argument special, but we already stripped key
    params = [("Dummy", "value")]
    for p in parts:
        i = p.find("=")
        if i >= 0:
            name = p[:i].strip().lower()
            value = p[i + 1 :].strip()
            params.append((name, native_str(value)))
    decoded_params = email.utils.decode_params(params)
    decoded_params.pop(0)  # get rid of the dummy again
    pdict = {}
    for name, decoded_value in decoded_params:
        value = email.utils.collapse_rfc2231_value(decoded_value)
        if len(value) >= 2 and value[0] == '"' and value[-1] == '"':
            value = value[1:-1]
        pdict[name] = value
    return key, pdict

def _encode_header(key: str, pdict: Dict[str, str]) -> str:
    """Inverse of _parse_header.

    >>> _encode_header('permessage-deflate',
    ...     {'client_max_window_bits': 15, 'client_no_context_takeover': None})
    'permessage-deflate; client_max_window_bits=15; client_no_context_takeover'
    """
    if not pdict:
        return key
    out = [key]
    # Sort the parameters just to make it easy to test.
    for k, v in sorted(pdict.items()):
        if v is None:
            out.append(k)
        else:
            # TODO: quote if necessary.
            out.append("%s=%s" % (k, v))
    return "; ".join(out)

def encode_username_password(
    username: Union[str, bytes], password: Union[str, bytes]
) -> bytes:
    """Encodes a username/password pair in the format used by HTTP auth.

    The return value is a byte string in the form ``username:password``.

    .. versionadded:: 5.1
    """
    if isinstance(username, unicode_type):
        username = unicodedata.normalize("NFC", username)
    if isinstance(password, unicode_type):
        password = unicodedata.normalize("NFC", password)
    return utf8(username) + b":" + utf8(password)

def doctests():
    # type: () -> unittest.TestSuite
    import doctest

    return doctest.DocTestSuite()

def split_host_and_port(netloc: str) -> Tuple[str, Optional[int]]:
    """Returns ``(host, port)`` tuple from ``netloc``.

    Returned ``port`` will be ``None`` if not present.

    .. versionadded:: 4.1
    """
    match = re.match(r"^(.+):(\d+)$", netloc)
    if match:
        host = match.group(1)
        port = int(match.group(2))  # type: Optional[int]
    else:
        host = netloc
        port = None
    return (host, port)

def qs_to_qsl(qs: Dict[str, List[AnyStr]]) -> Iterable[Tuple[str, AnyStr]]:
    """Generator converting a result of ``parse_qs`` back to name-value pairs.

    .. versionadded:: 5.0
    """
    for k, vs in qs.items():
        for v in vs:
            yield (k, v)

_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
_QuotePatt = re.compile(r"[\\].")
_nulljoin = "".join

def _unquote_cookie(s: str) -> str:
    """Handle double quotes and escaping in cookie values.

    This method is copied verbatim from the Python 3.5 standard
    library (http.cookies._unquote) so we don't have to depend on
    non-public interfaces.
    """
    # If there aren't any doublequotes,
    # then there can't be any special characters.  See RFC 2109.
    if s is None or len(s) < 2:
        return s
    if s[0] != '"' or s[-1] != '"':
        return s

    # We have to assume that we must decode this string.
    # Down to work.

    # Remove the "s
    s = s[1:-1]

    # Check for special sequences.  Examples:
    #    \012 --> \n
    #    \"   --> "
    #
    i = 0
    n = len(s)
    res = []
    while 0 <= i < n:
        o_match = _OctalPatt.search(s, i)
        q_match = _QuotePatt.search(s, i)
        if not o_match and not q_match:  # Neither matched
            res.append(s[i:])
            break
        # else:
        j = k = -1
        if o_match:
            j = o_match.start(0)
        if q_match:
            k = q_match.start(0)
        if q_match and (not o_match or k < j):  # QuotePatt matched
            res.append(s[i:k])
            res.append(s[k + 1])
            i = k + 2
        else:  # OctalPatt matched
            res.append(s[i:j])
            res.append(chr(int(s[j + 1 : j + 4], 8)))
            i = j + 4
    return _nulljoin(res)

def parse_cookie(cookie: str) -> Dict[str, str]:
    """Parse a ``Cookie`` HTTP header into a dict of name/value pairs.

    This function attempts to mimic browser cookie parsing behavior;
    it specifically does not follow any of the cookie-related RFCs
    (because browsers don't either).

    The algorithm used is identical to that used by Django version 1.9.10.

    .. versionadded:: 4.4.2
    """
    cookiedict = {}
    for chunk in cookie.split(str(";")):
        if str("=") in chunk:
            key, val = chunk.split(str("="), 1)
        else:
            # Assume an empty name per
            # https://bugzilla.mozilla.org/show_bug.cgi?id=169091
            key, val = str(""), chunk
        key, val = key.strip(), val.strip()
        if key or val:
            # unquote using Python's algorithm.
            cookiedict[key] = _unquote_cookie(val)
    return cookiedict


Q:

Kubernetes/Container engine HTTP API for creating pods

I need to create pods using HTTP.
I created cluster using:
gcloud preview container clusters create

and I can create pod using:
gcloud preview container pods create

But this is not what i want. I want to create pods with instance of my application via http API.
There is API where I can create clusters. But I need something like this for creating pods. Is it possible?

A:

This is basically the same question as this one (asked yesterday) and has the same answer:
Once you launch your container cluster on Google Container Engine, you will have a master running the kubernetes API on a VM in your GCP project. You can interact with it via the kubectl command line client, or you can extract the credentials for the cluster using gcloud and make HTTPS (but not HTTP) calls using the REST API exposed by the master.


Introduction {#S10}
============

The COVID-19 pandemic has resulted in substantial disruptions to healthcare delivery due to resource limitations, supply chain disruptions, the need to protect or backfill sickened healthcare workers, social distancing, and the realities of meeting surge demands. Many healthcare systems have responded by canceling or delaying elective surgical procedures.^[@R1]--[@R4]^ The downstream impacts on public health due to delays in diagnostic and therapeutic procedural care are unknown. The Lancet Commission on Global Surgery identified a profound gap in the availability of safe anesthetic and surgical care in low- and middle-income countries (LMIC), with an estimated 4.8 billion people lacking access to surgery at baseline.^[@R5],[@R6]^ While high-income countries (HICs) are better suited to absorb disruptions in surgical care, the effect of the COVID-19 pandemic on unmet needs in LMICs could be devastating. The time course of recovery to baseline conditions following the pandemic may also be prolonged due to depletion of healthcare resources in LMICs due to their limited capacity for resource recovery.

Assessing the volume of global surgical care is notoriously difficult. Prior work in this area has relied on estimation based on modeling and labor-intensive retrospective analysis of data from nations where such information is routinely recorded and available.^[@R5],[@R7]--[@R9]^ No real-time data sources for LMIC surgical volume are presently available.

We previously developed a free anesthesia calculator mobile application (app) for the Android platform. Called "Anesthesiologist", it has been in use since 2011 and has garnered worldwide adoption, with over two hundred thousand users from nearly every country in the world.^[@R10]^ As previously described, the primary user base of this application are physician anesthesiologists and other anesthesia providers.^[@R10]^

The app provides practical information about airway management and drug dosing and is used for decision support in the clinical care of patients. The app has substantially greater use in LMICs as compared to HICs.^[@R10]^ Our goal in this study was to determine if utilization of the app, aggregated over the large existing international user base, could serve as a real-time qualitative proxy for surgical case volume, which could be used to monitor the impact of, and recovery from, the COVID-19 pandemic.

Methods {#S11}
=======

Ethics Approval and Manuscript Preparation {#S12}
------------------------------------------

The study was reviewed and approved by the Emory University Institutional Review Board (study number 00082571), and there is a reliance agreement in place with the University of Washington Institutional Review Board. The approval includes a waiver of written informed consent. Participants anonymously gave electronic consent before participating in any data collection. The app is a medical device that falls into the category of enforcement discretion per the United States Food and Drug Administration.^[@R11]^ This manuscript was prepared in accordance with the STROBE checklist for improved reporting of outcomes from observational studies.^[@R12]^

Data Collection {#S13}
---------------

Data collection using the app has previously been described.^[@R10],[@R13]^ In brief, the app provides anesthesia references and drug calculation capabilities, and following development was deployed on the Google Play Store platform.^[@R14],[@R15]^ The app provides us with integrated data regarding service utilization and as well as responses to user surveys.^[@R16]^ The anonymized information collected includes timestamps from the mobile devices, time zone information, basic demographics, user location based on three sources (global positioning system; internet protocol address^[@R17]^; subscriber identity module country code), and app usage patterns. This data is collected and collated as previously described.^[@R10],[@R13]^ The approach to survey data collection allowed users to opt out at any time, resulting in survey fatigue and missing data, impacting missingness for demographic data collected.^[@R18]^ Data analyzed in the present work were collected between October 1, 2018 and April 18, 2020 (final full day of data collection). The electronic data warehouse at the University of Washington and at Seattle Children's Hospital were queried for aggregate surgical case counts which were used for the generation of panels shown in [Online Supplement, Figure S2](#SD1){ref-type="supplementary-material"} and [Figure S3](#SD1){ref-type="supplementary-material"}.

Statistical Methods {#S14}
-------------------

Raw data were downloaded and analyzed in R using RStudio v3.6.2 (R Core Team, Vienna, Austria).^[@R19],[@R20]^ Data containing invalid timestamps were removed (280 of 4,300,975 observations). Time series data were calculated from incoming individual data points (including logged app uses and in-app navigation) from all users. World Bank classification of country income level and region was made based on publicly available classification as of April 2020.^[@R21]^ The European Centers for Disease Control was queried for data related to global COVID-19 impact (case counts, death counts).^[@R22]^ Muslim-majority countries were defined as the set of 38 countries with greater than 80% of the population characterized as observing Islam ([Online Supplement, Table 1](#SD1){ref-type="supplementary-material"}). Change point detection in time series data were performed with the cpm package using raw time series data.^[@R23]^ For clarity of presentation, plots in [Figure 1](#F1){ref-type="fig"} were generated using a 7 day moving average to mitigate the routine effect of app use reductions occurring on weekends ([Online Supplement, Figure S1](#SD1){ref-type="supplementary-material"}). Country-level reductions in rate of app use were generated by (a) taking the mean daily counts for April 13, 2020 through April 18, 2020; (b) taking the mean daily counts of app use from Sep 1, 2020 through Nov 1, 2020; and then calculating (a)/(b), yielding the app use for the most recent data available as a percent of baseline use. The choropleth in [Online Supplement, Figure S4](#SD1){ref-type="supplementary-material"} was generated using the tmap package for R.^[@R24]^

Results {#S15}
=======

Demographics {#S16}
------------

From October 1, 2018 through April 18, 2020, there 4,300,975 data points collected and analyzed from 92,878 unique users. Provider demographics are summarized in [Table 1](#T1){ref-type="table"}. Incomplete survey completion resulted in partial data available from some users. The majority of users were anesthesia providers: physicians, certified registered nurse anesthetists, or anesthesiologist assistants. As discussed in a previous publication, anesthesia officers practicing in low-income countries may have identified as technically trained in anesthesia or otherwise self-identified.^[@R10]^ There was a wide distribution in terms of self-reported elements of practice environment. The distribution of participant characteristics are consistent with previous findings.^[@R10]^

Variation in App Use with Known Markers of Surgical Case Volume {#S17}
---------------------------------------------------------------

[Figure 1a](#F1){ref-type="fig"} demonstrates that there was consistent data collection from users over the study period. In the US ([Figure 1b](#F1){ref-type="fig"}), reductions in app use occurred on major holidays, including the period around US Thanksgiving Day (i.e., the fourth Thursday of November) and Christmas Day. This is consistent with case volume data obtained from University of Washington Medical Center and Seattle Children's Hospital ([Online Supplement, Figure S2](#SD1){ref-type="supplementary-material"}) and has been previously described in the literature as well.^[@R25]^ In Muslim-majority countries ([Figure 1c](#F1){ref-type="fig"}), no decrease was seen on US Thanksgiving Day, and a smaller decrease was observed around Christmas Day, but there were large reductions in app use during the month of Ramadan and around the time of Hajj. In Brazil ([Figure 1d](#F1){ref-type="fig"}), which has a large app user base, there was a notable decrease in app use during the Carnival celebration in 2019 and 2020 and the Corpus Christi celebration in 2019. There was also expected variation in app use by day of week, consistent with known data demonstrating that surgical case volumes are highest during the middle of the week and much lower on weekends ([Online Supplement, Figure S3](#SD1){ref-type="supplementary-material"}).^[@R26]^

Impact of COVID-19 on Surgical Case Volumes {#S18}
-------------------------------------------

The impacts of COVID-19 on app use are also illustrated in [Figure 1](#F1){ref-type="fig"}. Notably, all regions demonstrate steep declines in app use following the WHO declaration of global pandemic status. In Muslim-majority countries, there may have been declines in app use slightly prior to this declaration. Globally, there does appear to be an increase in app use that occurred in the last several days of data included in this sample.

We further parsed this global data to illustrate variability in app use relative to COVID-19 case counts. Overall, app use declined as COVID-19 cases mounted during the examined dates ([Figure 2](#F2){ref-type="fig"}). Region-specific findings include:

1.  In Sub-Saharan Africa ([Figure 2a](#F2){ref-type="fig"}) and World Bank Low Income ([Figure 2b](#F2){ref-type="fig"}) countries, the decrease in app use continues on a downward trend. The number of daily COVID-19 cases in Sub-Saharan is likely vastly underestimated due to lack of testing.

2.  In the East Asia and Pacific World Bank region ([Figure 2c](#F2){ref-type="fig"}), a decrease in app use in February was concordant with the initial rise of new cases in that region. App use rebounded and then decreased again following the WHO declaration.

3.  In World Bank High Income Countries ([Figure 2d](#F2){ref-type="fig"}; [Figure 3a](#F3){ref-type="fig"},[b](#F3){ref-type="fig"},[d](#F3){ref-type="fig"}), the reductions in app use appears to have plateaued, with a possible recent increase in app use.

4.  In Italy ([Figure 3a](#F3){ref-type="fig"}) and Germany ([Figure 3b](#F3){ref-type="fig"}), decreases in app use were seen well before the WHO pandemic declaration. By that time, the app use in Italy had already reached its current plateau. The decrease in app use in Germany was not nearly as pronounced as in Italy.

5.  In India ([Figure 3c](#F3){ref-type="fig"}) and the USA ([Figure 3d](#F3){ref-type="fig"}), decreases in app use followed the WHO declaration and were abrupt. App use in India appears to be increasing; has reached a plateau in the US.

A choropleth of the global impact of COVID-19 on app use was generated ([Online Supplement, Figure S4](#SD1){ref-type="supplementary-material"}) and further demonstrates the near universal reductions in app use observed. Overall, by country there was a median global reduction in app to 58% of baseline (interquartile range, 46%−75%), with reductions as low as 11% of baseline use in some countries. There was no clear relationship between overall reduction in app use and COVID-19 case count at the end of the study period in the aggregate ([Online Supplement Figure S5](#SD1){ref-type="supplementary-material"}). When examining the cross-correlation function of COVID-19 case count versus reductions in app use in individual regions and countries, there was strong inverse correlation between these two time series at very low lags (not shown).

Discussion {#S19}
==========

In the present work, we demonstrate that app metadata provides a low-cost, novel approach to the qualitative assessment of surgical case volume, particularly in countries where such real-time data may be difficult or impossible to otherwise obtain. The substantial reductions in app use observed in relation to major holidays and day of week correlate well with known impacts on surgical case volumes.^[@R25]--[@R28]^ This methodology similarly demonstrated previously unreported but plausbile reductions in surgical case volumes during Ramadan and Hajj in Muslim-majority countries and during Corpus Christi and Carnival in Brazil. As a proxy for surgical case volumes, this app data demonstrates expected but troubling impacts of the COVID-19 pandemic worldwide.

Globally, surgical case volumes are difficult to assess.^[@R7],[@R8]^ The difficulties in obtaining this and related data have been identified as a major challenge by the Lancet Commission on Global Surgery in the seminal Global Surgery 2030 report.^[@R6]^ In a recent modeling study of global surgical volumes, less than a third of countries could offer data to support the analysis.^[@R7]^ Even where such data are available from public health ministries, it is not available in real-time and may be limited to healthcare delivered by governmentally funded facilities.^[@R29]^

The benefits and challenges associated with collection of healthcare data using mobile technology have been previously discussed.^[@R30],[@R31]^ Benefits include a decentralized approach to collection of data from the 95% of the global population living in an area covered and subscribed to mobile cellular service.^[@R32]^ Specifically in LMIC, there were 5.5 billion mobile phone subscriptions, representing nearly 92 subscriptions per 100 inhabitants.^[@R33]^ Challenges include dissemination of specific applications, the types of data that can be collected, the tradeoff between apps that have clinical utility and the data that can be gleaned from the use of those apps, the availability of multiple platforms (e.g. Android, iOS), and the analysis and interpretation of stochastic app use data.

The impact of the COVID-19 pandemic on surgical capacity in LMIC may be dire. A large number of people are without adequate access to safe anesthetic and surgical care at baseline.^[@R6]^ There is the potential that this pandemic will further deplete resources of already-stressed LMIC healthcare systems, worsening access to surgical care. Our data indicate that the impacts of COVID-19 on surgical volumes have been substantial; it will be of great value for governments, global health organizations, and philanthropic organizations to have access to data providing markers of recovery of surgical capacity \--or lack thereof \-- in LMIC. Global strategies could be put in place by large organizations (such as the WHO) to attempt mitigation. The lack of association between COVID-19 case counts and reductions in app use are likely explained by the fact that hospital and governmental policies are likely driving surgical scheduling behavior to a greater extent than the actual patient surge in most places.

Practical utilization of the app from which data were gathered and analyzed is a specific strength of the present work. Users download and use the app for the clinical care of patients. The app was never advertised, nor was its use encouraged, through notifications or other mechanisms. Thus, app use reflects, on an individual basis, stochastic clinical care events. This same stochasticity highlights a limitation of the work, namely that data from individual regions or countries with a small user base reduces the confidence we can assign to the association between app use time series data and surgical case volume. Fortunately for global health purposes, our previous work demonstrates that, in LMIC, the app has greater frequency of use as compared to HIC.^[@R27]^

Another important limitation of the present work is the qualitative nature of the interpretation that can be made of shifts in app use. The app is used primarily for the care of pediatric cases (\~75% of app uses are for patients age 12 and younger), and users also consult the app during emergencies.^[@R34]^ Thus, the pattern of app use is greater on weekends (when a greater proportion of cases are emergencies) than would be expected based on the actual proportion of surgical case volumes comparing weekdays to weekends. This pediatric clinical predominance may drive greater use of the app in LMIC where (a) subspecialty training in pediatric anesthesia is less prevalent compared to HIC and (b) as high as 50% of the population may be under the age of 16. Patterns in app use will additionally be biased towards impacts specifically on pediatric case volumes. Notably, these needs in LMIC are not trivial: 1.7 billion children lack adequate access to surgical care and an estimated 85% of children in LMIC will need surgical care by age 16.^[@R35]^ Conversely, app utilization patterns may be relatively less impacted in HIC with dedicated pediatric hospitals. Given the differential impact of SARS-CoV-2 in the young versus the aged, and the proportion of elective versus non-elective surgery in pediatric patients, a greater degree of pediatric surgery may be seen compared to surgery for adults.

In conclusion, we present a real-time qualitative monitor of the impact of COVID-19 on global surgical volumes, particularly in at-risk LMICs. There is currently a clear trend of decreased surgical volume in certain regions associated with the onset of the COVID-19 pandemic. The trends in different regions will likely change with time depending on pandemic surge timing, restoration of normal operations, baseline healthcare capacity, and resource allocation. Combined with other information sources, this tool provides the opportunity to monitor and act in real time on data to identify areas continuing to suffer from depressed surgical volumes and direct resources to areas with the most need. A strategic global approach will decrease the morbidity and disability associated with diminished surgical capacity. Longer term, this tool can be combined with other data to assist with measurement of global surgical capacity as part of the Global Surgery 2030 initiative. As such, a near real-time dashboard has been developed ([http://globalcases.info](http://globalcases.info/)) and deployed as part of these efforts to ensure ongoing accessibility of this information.

Supplementary Material {#SM1}
======================

The authors would like to thank Alexandra Torborg, MBChB (University of KwaZulu-Natal, Nelson R Mandela School of Medicine) for insightful thoughts and comments on the work and the manuscript.

Funding and Conflicts of interest:

All authors declare: no financial relationships with any organizations that might have an interest in the submitted work in the previous three years; no other relationships or activities that could appear to have influenced the submitted work. The Anesthesiologist app was initially released in 2011 by Vikas O'Reilly-Shah with advertising in the free version and a paid companion app to remove the ads. The app intellectual property was transferred to Emory University in 2015 and advertisements were subsequently removed, and the companion app to remove ads made freely available for legacy users not updating to the ad-free version. Following review by the Emory University Research Conflict of Interest Committee, Vikas O'Reilly-Shah was released from any conflict of interest management plan or oversight.

**Sources of support**: NIH/NIGMS (T32 GM086270-11, DRL

I, Vikas O'Reilly-Shah, had full access to all the data in the study and had final responsibility to submit for publication.

![Time series data demonstrating app use for (a) all users and in specific regions: (b) USA, (c) Muslin-predominant countries, (d) Brazil. Dates of key holidays are indicated by vertical dashed lines.](nihpp-2020.05.03.20086819-f0001){#F1}

![The impact of COVID-19 on app use as a proxy for surgical case volumes, plotted with daily new COVID-19 case counts, in specific groups of countries as defined by the World Bank: (a) Sub-Saharan Africa, (b) World Bank Low income countries, (c) East Asia & Pacific, (d) World Bank High income countries.](nihpp-2020.05.03.20086819-f0002){#F2}

![The impact of COVID-19 on app use as a proxy for surgical case volumes, plotted with daily new COVID-19 case counts, in specific countries: (a) Italy, (b) Germany, (c) India, (d) USA.](nihpp-2020.05.03.20086819-f0003){#F3}

######

App user characteristics.

  --------------------------------------------------------------------------------
  n\                                                             Overall\
  Provider (%)                                                   92878
  -------------------------------------------------------------- -----------------
  Physician Attending/Consultant                                 12120 (26.1)

  Physician Resident/Fellow/Registrar                            9897 (21.3)

  CRNA or AA                                                     11619 (25.0)

  CRNA or AA Trainee                                             2134 (4.6)

  Technically Trained In Anesthesia                              1265 (2.7)

  Anesthesia Tech                                                2810 (6.1)

  Medical Student                                                2095 (4.5)

  Nurse                                                          1569 (3.4)

  Paramedic EMT                                                  996 (2.1)

  Respiratory Therapist                                          340 (0.7)

  Pharmacist                                                     395 (0.9)

  Other                                                          726 (1.6)

  Not Medical Practitioner                                       449 (1.0)

  *Length Of Practice (mean (SD))*                               *13.20 (13.41)*

  *Practice Model (%)*

  Physician only                                                 6199 (30.8)

  Physician supervised, anesthesiologist on site                 8416 (41.8)

  Physician supervised, non-anesthesiologist physician on site   1856 (9.2)

  Physician supervised, no physician on site                     1194 (5.9)

  No physician supervision                                       1348 (6.7)

  Not an anesthesia provider                                     1120 (5.6)

  *Practice Type (%)*

  Private clinic or office                                       4185 (19.7)

  Local health clinic                                            1954 (9.2)

  Ambulatory surgery center                                      1373 (6.5)

  Small community hospital                                       2602 (12.3)

  Large community hospital                                       5810 (27.4)

  Academic department/University hospital                        5313 (25.0)

  *Practice Size (%)*

  I am the only practitioner for large area                      7075 (28.0)

  One of several practitioners in the area                       4893 (19.4)

  Group practice 1--5 members                                    3341 (13.2)

  Group 5--10 members                                            2625 (10.4)

  Group 10--25 members                                           2467 (9.8)

  Group 25--50 members                                           2012 (8.0)

  Group \> 50 members                                            2848 (11.3)
  --------------------------------------------------------------------------------

CRNA=Certified Registered Nurse Anesthetist; AA=Anesthesiologist Assistant; EMT = Emergency Medical Technician

###### Key Points

Question {#S21}
========

Can usage data from a globally used anesthesiology calculator mobile application contribute to monitoring of the impacts to global surgical case volume caused by the COVID-19 pandemic, particularly in resource-limited environments such as World Bank low income countries?

Findings {#S22}
========

In this ongoing observational cohort study, application usage data from 92,878 unique users in 221 countries was found to serve well as a qualitative proxy for surgical case volume, with clear impacts to app use during weekend, holidays, and during the COVID-19 pandemic.

Meaning {#S23}
=======

This proxy of surgical volume will provide insight into the impact of and recovery from the COVID-19 pandemic where official data collection is not currently practical. A real time dashboard tracking this proxy of global surgical volume is live and under continued development.

[^1]: Author contributions:

    All authors meet ICMJE criteria for authorship.

    V-OS conceived the work, designed data collection tools, monitored data collection, wrote the statistical analysis plan, cleaned and analysed the data, and drafted and revised the manuscript. He is guarantor.

    VM interpreted the data, and drafted and revised the manuscript.

    FME interpreted the data and revised the manuscript.

    JES interpreted the data and revised the manuscript.

    WVC interpreted the data, and revised the manuscript.

    DRL analyzed the data, interpreted the data, and revised the manuscript.

    NJK interpreted the data and revised the manuscript.

    EH interpreted the data and revised the manuscript.

    CSJ interpreted the data and revised the manuscript.


In the manufacture of sheets of paper, and especially the manufacture of xerographic copy paper, it is desirable to test the paper for curl in order to determine that the paper is of a desired flatness. Generally, two types of curl may arise in paper manufacture as a result of moistening or drying a paper sheet. Simplex or basic curl is that which arises in one direction, such as results when a sheet of paper is rolled into a cylindrical tube. Diagonal curl is the result of a twist in a sheet of paper. For example, if the top edge of a sheet of paper is axially rotated in one direction and the bottom edge of the sheet is axially rotated in the opposite direction, the twist in the paper will produce a diagonal curl.
Current curl determination during paper production is generally performed manually and suffers from subjective variables owing to different determinations from competent testing personnel. Such things as a dominant eye, minor astigmatism, or the height of an individual tester are all variables that affect curl testing results.
The present invention relates to a unique method and apparatus for opto-electrically measuring the curl in a suspended sheet of paper to provide reliable, uniform test results.


LLVM Weekly - #299, September 23rd 2019

Welcome to the two hundred and ninety-ninth issue of LLVM Weekly, a weekly newsletter (published every Monday) covering developments in LLVM, Clang, and related projects. LLVM Weekly is brought to you by Alex Bradbury. Subscribe to future issues at http://llvmweekly.org and pass it on to anyone else you think may be interested. Please send any tips or feedback to asb@asbradbury.org, or @llvmweekly or @asbradbury on Twitter.

News and articles from around the web

LLVM 9.0.0 has been released. Amongst other highlights, the RISCV backend has been promoted from "experimental" to "official".

A new post on the LLVM blog details work to support cross-language LTO between Rust and C/C++.

The Women in Compilers and Tools Workshop held just before the LLVM Dev Meeting in October will feature a talk from April Wenssel on Contributing with Compassion.

On the mailing lists

LLVM commits

The first in a series of patches adding support for the data dependence graph has landed. r372162.

The LLVM docs gained a new "Getting Involved" article. r372487.

The Attributor now supports Heap-to-Stack conversion. r371942.

bugpoint gained support for -Oz and fixed -Os support. r372079.

The machine scheduler was enabled for the RISC-V backend, albeit with no target-specific model yet. r372106.

update_llc_test_checks.py gained initial support for MIR inputs. r372277.

It's now possible to specify a different encoding for a given instruction depending on HwMode. r372320.

Clang commits

The Clang user manual gained a new section on controlling floating point behaviour. r372180.

Clang now accepts -mcmodel={medany,medlow} for RISC-V, which are what LLVM calls 'small' and 'medium' code models. r372080.

clangd gained support for semantic selections. r371976.

Other project commits


NEW YORK (AP) — Actress-director Penny Marshall is being remembered for her wit and the ground she broke in the male-dominated film business.Marshall, who rose to fame as one of the stars of the..Credit: New Zealand Herald - Published 7 minutes ago

A trove of hacked EU diplomatic cables made available to the New York Times revealed no major secrets, but displayed the remarkably poor protection of routine exchanges among EU officials Hackers..Credit: EurActiv - Published 20 minutes ago

In his December 11, 2018 Intellectual Takeout article, Is New York City Entering a Pension Death Spiral like Chicago? broker-dealer recruiter Jon Henschen of Henschen & Associates looks at the issue of..Credit: PRWeb - Published 31 minutes ago

Carnegie Council presents its most popular resources created in 2018, including solutions to inequality, Russian influence in France, democracy in danger, the situation in Burma/Myanmar, artificial..Credit: PRWeb - Published 31 minutes ago

Good morning! This is the tech news you need to know this Wednesday.
1. *Internal Facebook documents show it gave tech giants like Amazon and Microsoft privileged access to user data, which Facebook..Credit: Business Insider - Published 36 minutes ago

Hackers accessed the European Union's diplomatic communications network for years, downloading cables that reveal concerns about the Trump administration, struggles to deal with Russia and China and..Credit: Reuters - Published 49 minutes agoAlso reported by •Reuters India

Three daredevils who danced and took videos at the top of a skyscraper in southwestern China were taken away by police in the latest incident of the fad known as rooftopping. The three men, all in..Credit: S.China Morning Post - Published 50 minutes ago

Michael Fassbender checks his phone as he waits for his ride outside of the Greenwich Hotel on Tuesday morning (December 18) in New York City. The 41-year-old actor tried to keep a low profile in a..Credit: Just Jared - Published 56 minutes ago

According to the report, the global gas sensors market was valued at approximately USD 1,936 million in 2017 and is expected to generate revenue of around USD 2,835 million by 2024, growing at a CAGR..Credit: GlobeNewswire - Published 2 hours ago

Matthew Morrison and his wife Renee pose for an adorable photo with son Revel while attending the Brooks Brothers and St. Jude Children’s Research Hospital Holiday Party on Tuesday (December 18) in..Credit: Just Jared - Published 2 hours ago

D'Angelo Russell collected 22 points and a career-high-tying 13 assists as the Brooklyn Nets overcame 36 points from LeBron James on Tuesday night and held on for a 115-110 victory over the Los Angeles..Credit: Reuters - Published 2 hours ago


Q:

Inter-arrival time of subsampled Poisson point process

Suppose that I draw $n$ points from a Poisson point process of rate $\lambda$, i.e. with inter-arrival times distributed i.i.d $\sim \text{Exp}(\lambda)$.
Now suppose that I choose $m < n$ of these points uniformly at random, and throw away the rest.
My question is as follows: What can be said about the inter-arrival times in this modified process? I.e. about the inter-arrival time between the $m$ remaining points?
A couple of related facts that I know:

if each point in the original point process is kept independently with probability $p$, then the "thinned" process has inter-arrival times i.i.d $\sim \text{Exp}(p \lambda)$
If the $m$ points are chosen i.i.d with a uniform distribution over some interval $[0,t]$, and then ordered, the inter-arrival times would be Beta distributed.

Somehow, neither of these observations seems to fit my case. Any pointer is appreciated!

A:

Let $n$ and $m<n$ be fixed. Consider first the random sampling without
replacement of $m$ values in $\{1,\,2,\, \dots,\,n\}$, leading to $m$
order statistics $I_1 < I_2 < \dots < I_m$ for the retained numbers and $m-1$ spacings $K_j:=
I_{j+1} -I_{j}$ for $j=1$ to $m-1$. With the conventions
$I_0:=0$ and $I_{m+1}:=n+1$, we define as well $K_0=I_1$ and $K_m=n+1-I_m$.
It is not too difficult to see that the $m + 1$
random variables $K_j$ have the same distribution, namely the distribution of $I_1$. Moreover
$$
   \text{Pr}\{ I_1 = k  \} =
 \frac{{n-k}\choose{m-1}}{{n}\choose{m} } := p_k
 \qquad (1 \leq k \leq n-m +1),
$$
since the number of 'favourable outcomes' is the number of choices
of $m-1$ items among $n-k$.
This allows the computation of the $p_k$ by the recursion
$$
   p_{k+1} = \frac{ n- k -m +1}{n-k} \, p_{k}
$$
for $k = 1$ to $n-m$, starting from a positive $p_1$
and then normalising to have total mass unity. Note that
$\mathbb{E}[K] = (n+1)/(m+1)$.
Now let us come back to the Poisson process and the "ensemble
thinning" of the question. If the $n$ arrivals are at
times $X_1 < X_2 < \dots < X_n$ the $m$ selected arrivals
are $Y_j:=X_{I_j}$ where the $I_j$ are as before. The interarrival
$W_j:=Y_{j+1}-Y_{j}$ is the sum of the random number $K_j$ of independent
exponential interarrivals $X_{i+1}-X_{i}$. Conditional on $K_j=k$ the r.v. $W_j$ is
Gamma with shape $k$ and rate $\lambda$. The unconditional
distribution of a $W$ is a mixture
$$
    W \sim \sum_{k=1}^{n-m+1} p_k \, \text{Gam}(k,\,\lambda).
$$
We implicitely supposed that the choice of the $m$ retained
arrivals is done independently of the $X_i$. Note that the $W_j$ are
not independent because the $K_j$ are not independent.

n <- 30; m <- 12

## Monte-Carlo simulation with 'N' replications ('lambda' is 1)
N <- 30000
set.seed(1234)

## compute the sample distributions for the new interarrivals 'W'
## and the intersampled 'K'
W <- matrix(NA, nrow = N, ncol = m - 1)
K <- matrix(NA, nrow = N, ncol = m + 1)
colnames(K) <- paste("K", 0:m, sep = "_")
colnames(W) <- paste("W", 1:(m - 1), sep = "_")

for (samp in 1:N) {
    X <- cumsum(rexp(n))                    ## old arrivals
    Isamp <- sort(sample(1:n, size = m))    ## indices for new arrivals
    K[samp, ] <- diff(c(0, Isamp, n + 1))   ## compute 'K' for checks
    W[samp, ] <- diff(X[Isamp])             ## new interarrivals
}

## compute the exact distribution of the r.vs 'K'
p <- rep(NA, n - m + 1)
p[1] <- 1
for (k in 1:(n - m)) {
    p[k + 1] <- p[k] * (n - k - m + 1) / (n - k)
}
p <- p / sum(p)

## compute the exact expectation of 'K' for check
EK <- sum(p * seq(from = 1, to = n - m + 1, by = 1))

## compare distribution of W_j, 'j' is between 0 and 'm'
j  <- 8
tab <- table(factor(K[ , j + 1], levels = 1:(n - m + 1)))
compar <- cbind(Sim = tab, Exact = round(p * N))
cols <-  c("SteelBlue", "orangered")
barplot(t(compar), beside = TRUE,
        legend.text = c(sprintf("Simul. K_%d", j), "Exact"),
        main = sprintf("Mixture weights p_k for n = %d, m = %d", n, m),
        col = cols)

## compare distributions functions for interarrivals
F_Sim <- ecdf(W[ , 1])

qx <- quantile(F_Sim)
x <- seq(from = qx["0%"], to = qx["100%"], length.out = 300)
F_Exact <- rep(0, length(x))
for (k in 1:(n - m + 1)) {
    F_Exact<- F_Exact+ p[k] * pgamma(x, shape = k)
}

plot(F_Sim, col = cols[1],
     main = "distribution function of the 'new' interarrival W_1",
     xlab = "w", ylab = "Fn(w)", lwd = 2)
lines(x, F_Exact, col = cols[2], lty = "dashed", lwd = 2)
legend("center", legend = c("Simul. (ECDF)", "Exact (mixt. of gammas)"),
       lty = c("solid", "dashed"), lwd = 2.5, col = cols)


Feel free to plagiarize at will

Donnie Vinson
Enron NetWorks
5-8177
donald.wayne.vinson@enron.com


A Kansas police department is facing allegations of racism after a black man used his cellphone to record two white officers yanking him out of a vehicle after seeing “vegetation” on his window.

Rudy Samuel, 31, live-streamed the encounter on Facebook earlier this month after Winfield police stopped his car. Winfield is a small city situated along the Walnut River in Cowley County, Kansas.

“If you want to see a video of racial profiling, this is the poster child right here,” Kansas City attorney Brian McCallister, who is not representing anyone in the case, told HuffPost on Monday. “It was an abuse of power because they had no reason to use that kind of force.”

In the video, recorded May 13, Samuel says he’s been stopped by officers who claim he failed to signal within 100 feet of making a turn. Samuel narrates the first few minutes of the video and waits as the officers attempt to verify his driver’s license and vehicle registration. About two and a half minutes into the video, an officer can be seen approaching Samuel’s vehicle.

“Hey, Mr. Samuel, what caught my attention was this vegetation stuff right here,” the officer said.

After looking at what the officer is referring to ― which isn’t visible in the video but is apparently some sort of vegetation in the window seal ― Samuel can be heard saying, “That’s tree stuff, man … that’s gotta be from a tree.”

He later adds: “I don’t even smoke.”

The officer then tells Samuel he’s going to put the vegetation in an evidence bag, and he’s seen returning to his patrol car. He then returns with another officer.

The first officer can be heard telling Samuel to step out of his vehicle. When Samuel asks why, the officer claims it’s because of the “vegetation in the window.” Asked by Samuel to test the vegetation, the officer replies, “I ain’t gotta test it right now. I need you to step out for me.”

The officers then appear to reach into the vehicle and drag Samuel out. Samuel drops his cellphone at this point, but the phone continues to record, capturing the sound of officers detaining Samuel.

Story continues

“You’re being detained so I can search the vehicle,” an officer can be heard saying. Samuel replies, “I give no permission to search that vehicle.”

Shortly thereafter, the video stops. Samuel later wrote on Facebook that one of the officers stopped the recording.

One of the officers who stopped Rudy Samuel on May 13. (Photo: Facebook)

Samuel, citing fears for his safety, directed HuffPost’s questions about the traffic stop to a man he identified as his spokesperson ― Peter Wright, of the Kansas-based Freedom 1 social justice group.

Wright said the officers detained Samuel until they finished searching his vehicle, then released him without a citation.

“This has happened to him before for no reason,” Wright said. “This young man has a good background. He’s a father, he’s engaged to be married and his grandfather is a minister. This was racially motivated.”

McCallister said police had no right to search Samuel’s vehicle.

“In order to search that car, they have to have reasonable suspicion that a crime is being committed or was committed,” the attorney explained. “That police officer is saying that the vegetation on the window is what gives him reasonable suspicion to search the car. In my opinion, that was pretext for searching the car.”

Winfield Police Chief Brett Stone declined to discuss the traffic stop, but did tell HuffPost it is “under investigation.”

Wright said Samuel is weighing his options and considering a lawsuit against the police department. “He has a biracial family and they’ve moved out of Winfield due to threats,” he said.

“He wants people to know what happened,” Wright added. “He’s lucky to be alive, because so many guys in situations like this are not able to tell their story.”

Send David Lohr an email or follow him on Facebook and Twitter.

Also on HuffPost

Love HuffPost? Become a founding member of HuffPost Plus today.

This article originally appeared on HuffPost.


If you follow the links through you'll find a sentence which indicates that if you meet their technical and creative standards the most likely outcome is that they'll offer you a contract whereby you pay them $50 per image to have as many images as you want , unedited, put in a "Photographers Choice Placement Fee collection" at a cost to you of $50 per image. A new slant on the stock agency business I guess- I think most of us kind of expect our agencies to pay us! Seriously from their point of view maybe not such a bad tactic. They have so many images and so many photographers that they really don't need too many more. This approach will either serve to discourage photographers from applying, or to contribute directly to Getty's profits if their applicants confidence/ego makes them hard to dissuade. Seems to to be a little similar to vanity publishing, and no harm in it so long as you go into it knowing that probably most images in libraries don't ever make $50, and that their most serious customers might well not browse collections where Getty have done no editing.

There has been a similar fee-based program for years, called 'photographer's choice", with the differences that
to participate into it, you needed to have already a "proper" contract with them, and that the number of
submissions were limited. At the time of its creation, the average return per image at Getty was much higher than
the fee (making it a good business decision to submit the maximum number of allowed images), but as of late, it
has eroded significantly due to the dilution that is pervasive in the industry.

Percentages don't matter. The only numbers worth talking about are royalties per image per year. Smaller libraries tend to lack the global distributor based coverage that the top agencies bring, and so whilst royalty percentages are larger sales are often much lower. Problem is that few libraries seem able or willing to talk about commission per image/year, and I don't know personally any stock library whose editors make acceptance decisions aided by revenue per image data, or feeds such data to photographers to guide their submissions.


Whistling Woods International launches animation school

MUMBAI:Whistling Woods International (WWI) has launched its animation training division, the Whistling Woods School of Animation (WWSOA), as a separate entity.

Established as a separate animation school under the WWI umbrella, the institute is now a brand by itself. The WWSOA also plans to start a range of new specialized courses catering to the needs of different animation aspirants. The courses range from short-term to long-term as well as part time and full time.

"It is a pleasure for us, following just four successful operating years, to announce WWSOA’s autonomy!Their growth is a tribute to the combined alchemy of animation’s phenomenal growth, India’s major role in enabling this expansion and our animation graduates being in such dynamic demand,” said Whistling Woods International president Meghna Ghai-Puri.

"WWSOA premieres its re-engineered animation school with a spectrum of new specialized courses catering to the needs of novice animation aspirants, students desiring to transfer into more aggressive and expansive training programs, as well as professionals seeking to sharpen and/or expand their abilities. The new entity is structured to allow students world-class animation instruction in a full-time five semester diploma manner, a part time new animation proficiency course, or anything in between", said Whistling Woods International dean John J. Lee, Jr..

WWSOA head of department Gokul K. said, "More specialized courses are the need of the hour. We have had many inquiries for career advice from students and parents for specialized courses. Students come and ask for a course in exclusively one field like matte painting, 2D or 3D etc. We are also in touch with filmmakers who say there is a dearth of quality VFX specialists."

The WWSOA also has seats for students who have studied elsewhere and working professionals, who want to further their education. They are given admissions based on their portfolios and after clearing a test, which means they need not study what they already know again.

"On the basis of market research as well as the inquiries we have received from students, parents and studios, we are coming out with new courses. We’re in our fourth year now and have decided to cater to a larger audience. For students looking to begin a career in animation there is the diploma in advanced animation, for which we are also increasing the batch size as we already have more than adequate infrastructure with two 2D labs from classic 2D animation and three 3D labs," added Gokul K.

The institute is offering the Diploma in The Art & Technique of Animation and the Advanced Diploma in The Art & Technique of Animation to students for its January 2010 intake. Besides that, other courses like ; Certificate Course in Basics of Animation, Certificate Course in Animation, Certificate Course in CG Animation , Diploma in The Art & Technique of Animation, Advanced Diploma in The Art & Technique of Animation, Certificate Course for ‘Specialisation’ (for working professionals),will also be offered from July 2010.


Hail to the Hugely Classy Commander-in-Chief

The results of the Iowa caucuses notwithstanding, there remains the strong possibility that Donald Trump could win the Republican nomination, and contest for the presidency. It’s time, in other words, for all Americans to think seriously about what sort of commander in chief Trump would be. It’s especially important given that, since 9/11, the executive branch has enjoyed tremendous freedom to undertake covert and clandestine operations, as well as expanded military commitments, with little restraint or oversight from Congress, the courts, or the American people.

Campaign slogans and sound bites — which tend to dissolve under the glare of day-to-day Oval Office decision making — are not normally the best evidence to rely on when considering a candidate’s future as a policymaker. But Trump leaves us little choice. He has largely kept hidden who, if anyone, provides him with military advice, preferring to mention anonymous generals, as well as a few specific individuals, like former U.N. Ambassador John Bolton and retired U.S. Army Col. Jack Jacobs. And his frequent refrain is that he trusts his own judgment, because he is such an exceptionally smart person. Taking him at his word, I recently read just about every one of his speeches or interview excerpts that dealt specifically with military affairs, in an attempt to discern his thinking on five critical areas of U.S. military policy.

First, what sort of force structure would President Trump develop? Here, he consistently warns that the U.S. military is presently “very weak,” has “never been so poorly prepared,” and is steadily “being decimated.” The solution to this troubling diagnosis is to build a military that is “bigger, better, and stronger,” as well as “beyond anything … technologically.… Because you’re getting now, militaries have to be technologically advanced.”

Trump never translates this into specifying how many Joint Strike Fighters he believes the government ought to buy, or what precisely the Army’s target strength should be. (When asked which of the three legs of the nuclear triad he prioritized, he replied, “I think, for me, nuclear is just the power, the devastation is very important to me.”) Rather, Trump’s envisioned force structure is “a military so strong, so big, so powerful that nobody messes with us. Nobody, nobody, nobody messes with us.” Without using the concepts specifically, Trump displays tremendous faith in the power of general deterrence and compellence against all current and potential adversaries. They will intuitively sense America’s power and either refrain from undertaking undesirable actions or change their policies once the U.S. military reaches a certain size.

President Trump also pledges to somehow cut defense spending while similarly building up the military, apparently by uncovering heretofore unknown efficiencies: “We can do it for a lot less. They sent a washer from South Carolina to Texas. It cost $997,000, okay?” In 2013, he also backed sequestration, but added that federal budget spending across the board needed to be cut much deeper.

Second, what uses of force would Trump authorize? When asked this in October, he replied, “The Trump doctrine is very simple, Jake. It’s strength. It’s strength. Nobody is going to mess with us.” Yet, on the rare occasions that interviewers pressed him further, he expressed a decidedly non-interventionist philosophy. He claims to have been opposed to the Vietnam War, “yet another disaster for our country, the second Gulf War, a huge mistake because you’re going to destabilize the whole Middle East,” and the 2011 Libya intervention — “look at what we did there — it’s a mess.” He believes that Iraq and Libya would be better off with Saddam Hussein and Muammar al-Qaddafi still in power, and that both leaders would have prohibited transnational terrorists from operating within their countries.

Trump takes a much stronger line against North Korea’s nuclear weapons program, noting: “We got to close it down, because he’s getting too close to doing something…. Once he has the transportation system [apparently referring to delivery vehicles], he’s sick enough to use it. So we better get involved.” When asked by CBS journalist Scott Pelley, “You would drop a bomb on their nuclear reactor?” (a mission Trump once endorsed), Trump replied, “I would do something.” Here, Trump apparently is unconcerned about the nuclear deterrent possessed by North Korea, or its vast conventional military power that would assuredly be unleashed against South Korea in the case of a U.S.-led preemptive strike on Pyongyang’s nuclear program

Third, what would Trump do about the Islamic State? Despite his expressed, selective opposition to other interventions, he repeatedly supports becoming much more deeply committed militarily in Iraq and Syria. He pledged in September, “I would end ISIS forcefully … you gotta knock ’em out. You gotta knock ’em out,” using another name for the Islamic State. Recently in a campaign video, he said, “We are going to get rid of them, and we are going to get rid of them fast.” This wholly improbable time frame is consistent with his self-image: “I’m the most military-based and the most militaristic person.”

He offers several, conflicting courses of action for achieving his objective of destroying the Islamic State. Trump supports putting more U.S. troops (without saying how many) on the ground in both Iraq and Syria. One mission that those ground troops would purportedly fulfill is providing humanitarian assistance to displaced Syrians, as he proclaimed, “I love the idea of building a safe zone someplace in Syria.” Inside of this “big, beautiful safe zone,” displaced persons “would be given shelter and food and be taken care of,” and with U.S. troops nearby, “ISIS wouldn’t want anything to do with it.”

Trump also endorses bombing Iraqi oilfields to eliminate one Islamic State source of wealth, declaring in November, “I would bomb the shit out of ’em. I would just bomb those suckers … I’d blow up the pipes … I’d blow up every single inch. There would be nothing left.” (Regarding the likely opposition from Baghdad he replies, “Who cares? I don’t care about the government of Iraq. They’re corrupt. The government of Iraq is totally corrupt.”) Yet, even while simultaneously bombing the entirety of Iraqi oil infrastructure, Trump has promised (for years) that he will simply “take their oil.” Presumably, he would seek to somehow remove the Islamic State’s oil from Iraq, before authorizing the destruction of its oilfields. Finally, he endorses allowing Russian President Vladimir Putin — “a very bright and talented man” — to undertake this robust mission on behalf of the United States: “Russia wants to get rid of ISIS. We want to get rid of ISIS. Maybe let Russia do it. Let ’em get rid of ISIS. What the hell do we care?”

Fourth, what sort of civil-military relations would characterize Trump’s presidency? Unlike other Republican candidates who vow to listen to their generals, Trump expresses a tremendous self-confidence in his own knowledge of military affairs. As he explained last year to a biographer, “[I] always felt that I was in the military,” and that attending New York Military Academy gave him “more training militarily than a lot of the guys that go into the military.” Trump wants his senior military advisers to remain in the shadows and refrain from conducting interviews or speaking publicly: “I don’t want generals that talk…. I want generals that have action.” As to what sorts of general and flag officers he would listen to, “I want Gen. George Patton. I want Gen. [Douglas] MacArthur.” Both make for curious choices — Patton is known to have struck soldiers under his command who were likely suffering from PTSD, while MacArthur was effectively fired by President Harry Truman for disobeying orders by championing an expansion of the Korean War into China.

Fifth, what sort of alliance structure would Trump support? He has a long and consistent track record of pledging to rebalance the burden sharing among America’s mutual defense treaty allies. To retain the 50+ year U.S. military presence in South Korea, he would insist that country “pay us a lot of money,” adding “I just ordered 4,000 television sets. They come from South Korea. South Korea is a money machine.… South Korea should pay us and pay us very substantially for protecting them.” Similarly, he would rethink the long-standing commitment to Japan, believing, “If we get attacked, Japan doesn’t have to help us. Somehow, that doesn’t sound so fair.” Finally, he declared in August: “I would support NATO,” but contends that in Ukraine, “I’m certainly not a fan of us being against Russia … for Ukraine when Germany is sitting back, you know, accepting all the oil and gas that they can get from Russia.”

It is difficult to summarize Commander in Chief Trump because most of his policy positions are not coherent, only roughly sketched out, and often contradict each other. He expresses an extremely dark and threatening worldview, believing, for example, that “[Iran is] going to take over parts of the world that you wouldn’t believe. And I think it’s going to lead to nuclear holocaust,” but he simultaneously champions reduced military commitments in most regions, except Iraq, Syria, and North Korea. He is equally a non-interventionist and a humanitarian interventionist, and someone who supports preventive uses of force. He suggests he would build up a bigger and more technologically advanced military, while simultaneously slashing defense spending.

Military officers complain anonymously about the prospects of Trump as commander in chief, as they have for previous candidates and actual presidents, but they can be expected to overwhelmingly follow orders rather than retire in droves. In my encounters with senior officers, most respond to his outrageous comments with a disbelieving eye roll and generally accept that candidates will say anything to get their party’s nomination and eventually elected. Moreover, those who have done multiple tours through the Pentagon recognize that the Office of the Secretary of Defense has far greater influence in the day-to-day operations and morale of the military than the president. Trump has never indicated who he would nominate to run the Pentagon, but he has indicated that he would nominate or appoint business leaders to keep diplomatic posts. (It should be noted that the last full-time business executive to become secretary of defense was Donald Rumsfeld, who was widely loathed by the officer corps.)

Of course, all of Trump’s words might be meaningless once he enters the White House. As he told a cheering crowd in Pella, Iowa, on Jan. 23, “When I’m president, I’m a different person. I can do anything.”

Johnny Louis/FilmMagic


To paraphrase the Black Belt Bayesian: Behind every exciting, dramatic failure, there is a more important story about a larger and less dramatic failure that made the first failure possible.

If every trace of religion was magically eliminated from the world tomorrow, then—however much improved the lives of many people would be—we would not even have come close to solving the larger failures of sanity that made religion possible in the first place.

We have good cause to spend some of our efforts on trying to eliminate religion directly, because it is a direct problem. But religion also serves the function of an asphyxiated canary in a coal mine—religion is a sign, a symptom, of larger problems that don't go away just because someone loses their religion.

Consider this thought experiment—what could you teach people that is not directly about religion, which is true and useful as a general method of rationality, which would cause them to lose their religions? In fact—imagine that we're going to go and survey all your students five years later, and see how many of them have lost their religions compared to a control group; if you make the slightest move at fighting religion directly, you will invalidate the experiment. You may not make a single mention of religion or any religious belief in your classroom, you may not even hint at it in any obvious way. All your examples must center about real-world cases that have nothing to do with religion.

If you can't fight religion directly, what do you teach that raises the general waterline of sanity to the point that religion goes underwater?

Here are some such topics I've already covered—not avoiding all mention of religion, but it could be done:

But to look at it another way—

Suppose we have a scientist who's still religious, either full-blown scriptural-religion, or in the sense of tossing around vague casual endorsements of "spirituality".

We now know this person is not applying any technical, explicit understanding of...

...what constitutes evidence and why;

...Occam's Razor;

...how the above two rules derive from the lawful and causal operation of minds as mapping engines, and do not switch off when you talk about tooth fairies;

...how to tell the difference between a real answer and a curiosity-stopper;

...how to rethink matters for themselves instead of just repeating things they heard;

...certain general trends of science over the last three thousand years;

...the difficult arts of actually updating on new evidence and relinquishing old beliefs;

...epistemology 101;

...self-honesty 201;

...etcetera etcetera etcetera and so on.

When you consider it—these are all rather basic matters of study, as such things go. A quick introduction to all of them (well, except naturalistic metaethics) would be... a four-credit undergraduate course with no prerequisites?

But there are Nobel laureates who haven't taken that course! Richard Smalley if you're looking for a cheap shot, or Robert Aumann if you're looking for a scary shot.

And they can't be isolated exceptions. If all of their professional compatriots had taken that course, then Smalley or Aumann would either have been corrected (as their colleagues kindly took them aside and explained the bare fundamentals) or else regarded with too much pity and concern to win a Nobel Prize. Could you—realistically speaking, regardless of fairness—win a Nobel while advocating the existence of Santa Claus?

That's what the dead canary, religion, is telling us: that the general sanity waterline is currently really ridiculously low. Even in the highest halls of science.

If we throw out that dead and rotting canary, then our mine may stink a bit less, but the sanity waterline may not rise much higher.

This is not to criticize the neo-atheist movement. The harm done by religion is clear and present danger, or rather, current and ongoing disaster. Fighting religion's directly harmful effects takes precedence over its use as a canary or experimental indicator. But even if Dawkins, and Dennett, and Harris, and Hitchens should somehow win utterly and absolutely to the last corner of the human sphere, the real work of rationalists will be only just beginning.