"""passlib.crypto.des -- DES block encryption routines

History
=======
These routines (which have since been drastically modified for python)
are based on a Java implementation of the des-crypt algorithm,
found at `<http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_.

The copyright & license for that source is as follows::

    UnixCrypt.java 0.9 96/11/25
    Copyright (c) 1996 Aki Yoshida. All rights reserved.
    Permission to use, copy, modify and distribute this software
    for non-commercial or commercial purposes and without fee is
    hereby granted provided that this copyright notice appears in
    all copies.

    ---

    Unix crypt(3C) utility
    @version 0.9, 11/25/96
    @author  Aki Yoshida

    ---

    modified April 2001
    by Iris Van den Broeke, Daniel Deville

    ---
    Unix Crypt.
    Implements the one way cryptography used by Unix systems for
    simple password protection.
    @version $Id: UnixCrypt2.txt,v 1.1.1.1 2005/09/13 22:20:13 christos Exp $
    @author Greg Wilkins (gregw)

The netbsd des-crypt implementation has some nice notes on how this all works -
    http://fxr.googlebit.com/source/lib/libcrypt/crypt.c?v=NETBSD-CURRENT
"""

# TODO: could use an accelerated C version of this module to speed up lmhash,
#       des-crypt, and ext-des-crypt

# core
import struct

# pkg
from passlib import exc

# local
__all__ = [
    "expand_des_key",
    "des_encrypt_block",
]


# masks/upper limits for various integer sizes
INT_24_MASK = 0xFFFFFF
INT_56_MASK = 0xFFFFFFFFFFFFFF
INT_64_MASK = 0xFFFFFFFFFFFFFFFF

# mask to clear parity bits from 64-bit key
_KDATA_MASK = 0xFEFEFEFEFEFEFEFE
_KPARITY_MASK = 0x0101010101010101

# mask used to setup key schedule
_KS_MASK = 0xFCFCFCFCFFFFFFFF


# placeholders filled in by _load_tables()
PCXROT = IE3264 = SPE = CF6464 = None


def _load_tables():
    """delay loading tables until they are actually needed"""
    global PCXROT, IE3264, SPE, CF6464  # noqa: PLW0603

    # ---------------------------------------------------------------
    # Initial key schedule permutation
    # PC1ROT - bit reverse, then PC1, then Rotate, then PC2
    # ---------------------------------------------------------------
    # NOTE: this was reordered from original table to make perm3264 logic simpler
    PC1ROT = (
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000002000,
            0x0000000000002000,
            0x0000000000000020,
            0x0000000000000020,
            0x0000000000002020,
            0x0000000000002020,
            0x0000000000000400,
            0x0000000000000400,
            0x0000000000002400,
            0x0000000000002400,
            0x0000000000000420,
            0x0000000000000420,
            0x0000000000002420,
            0x0000000000002420,
        ),
        (
            0x0000000000000000,
            0x2000000000000000,
            0x0000000400000000,
            0x2000000400000000,
            0x0000800000000000,
            0x2000800000000000,
            0x0000800400000000,
            0x2000800400000000,
            0x0008000000000000,
            0x2008000000000000,
            0x0008000400000000,
            0x2008000400000000,
            0x0008800000000000,
            0x2008800000000000,
            0x0008800400000000,
            0x2008800400000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000040,
            0x0000000000000040,
            0x0000000020000000,
            0x0000000020000000,
            0x0000000020000040,
            0x0000000020000040,
            0x0000000000200000,
            0x0000000000200000,
            0x0000000000200040,
            0x0000000000200040,
            0x0000000020200000,
            0x0000000020200000,
            0x0000000020200040,
            0x0000000020200040,
        ),
        (
            0x0000000000000000,
            0x0002000000000000,
            0x0800000000000000,
            0x0802000000000000,
            0x0100000000000000,
            0x0102000000000000,
            0x0900000000000000,
            0x0902000000000000,
            0x4000000000000000,
            0x4002000000000000,
            0x4800000000000000,
            0x4802000000000000,
            0x4100000000000000,
            0x4102000000000000,
            0x4900000000000000,
            0x4902000000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000040000,
            0x0000000000040000,
            0x0000020000000000,
            0x0000020000000000,
            0x0000020000040000,
            0x0000020000040000,
            0x0000000000000004,
            0x0000000000000004,
            0x0000000000040004,
            0x0000000000040004,
            0x0000020000000004,
            0x0000020000000004,
            0x0000020000040004,
            0x0000020000040004,
        ),
        (
            0x0000000000000000,
            0x0000400000000000,
            0x0200000000000000,
            0x0200400000000000,
            0x0080000000000000,
            0x0080400000000000,
            0x0280000000000000,
            0x0280400000000000,
            0x0000008000000000,
            0x0000408000000000,
            0x0200008000000000,
            0x0200408000000000,
            0x0080008000000000,
            0x0080408000000000,
            0x0280008000000000,
            0x0280408000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000010000000,
            0x0000000010000000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000010001000,
            0x0000000010001000,
            0x0000000040000000,
            0x0000000040000000,
            0x0000000050000000,
            0x0000000050000000,
            0x0000000040001000,
            0x0000000040001000,
            0x0000000050001000,
            0x0000000050001000,
        ),
        (
            0x0000000000000000,
            0x0000001000000000,
            0x0000080000000000,
            0x0000081000000000,
            0x1000000000000000,
            0x1000001000000000,
            0x1000080000000000,
            0x1000081000000000,
            0x0004000000000000,
            0x0004001000000000,
            0x0004080000000000,
            0x0004081000000000,
            0x1004000000000000,
            0x1004001000000000,
            0x1004080000000000,
            0x1004081000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000080,
            0x0000000000000080,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080080,
            0x0000000000080080,
            0x0000000000800000,
            0x0000000000800000,
            0x0000000000800080,
            0x0000000000800080,
            0x0000000000880000,
            0x0000000000880000,
            0x0000000000880080,
            0x0000000000880080,
        ),
        (
            0x0000000000000000,
            0x0000000008000000,
            0x0000002000000000,
            0x0000002008000000,
            0x0000100000000000,
            0x0000100008000000,
            0x0000102000000000,
            0x0000102008000000,
            0x0000200000000000,
            0x0000200008000000,
            0x0000202000000000,
            0x0000202008000000,
            0x0000300000000000,
            0x0000300008000000,
            0x0000302000000000,
            0x0000302008000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000400000,
            0x0000000000400000,
            0x0000000004000000,
            0x0000000004000000,
            0x0000000004400000,
            0x0000000004400000,
            0x0000000000000800,
            0x0000000000000800,
            0x0000000000400800,
            0x0000000000400800,
            0x0000000004000800,
            0x0000000004000800,
            0x0000000004400800,
            0x0000000004400800,
        ),
        (
            0x0000000000000000,
            0x0000000000008000,
            0x0040000000000000,
            0x0040000000008000,
            0x0000004000000000,
            0x0000004000008000,
            0x0040004000000000,
            0x0040004000008000,
            0x8000000000000000,
            0x8000000000008000,
            0x8040000000000000,
            0x8040000000008000,
            0x8000004000000000,
            0x8000004000008000,
            0x8040004000000000,
            0x8040004000008000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000004000,
            0x0000000000004000,
            0x0000000000000008,
            0x0000000000000008,
            0x0000000000004008,
            0x0000000000004008,
            0x0000000000000010,
            0x0000000000000010,
            0x0000000000004010,
            0x0000000000004010,
            0x0000000000000018,
            0x0000000000000018,
            0x0000000000004018,
            0x0000000000004018,
        ),
        (
            0x0000000000000000,
            0x0000000200000000,
            0x0001000000000000,
            0x0001000200000000,
            0x0400000000000000,
            0x0400000200000000,
            0x0401000000000000,
            0x0401000200000000,
            0x0020000000000000,
            0x0020000200000000,
            0x0021000000000000,
            0x0021000200000000,
            0x0420000000000000,
            0x0420000200000000,
            0x0421000000000000,
            0x0421000200000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000010000000000,
            0x0000010000000000,
            0x0000000100000000,
            0x0000000100000000,
            0x0000010100000000,
            0x0000010100000000,
            0x0000000000100000,
            0x0000000000100000,
            0x0000010000100000,
            0x0000010000100000,
            0x0000000100100000,
            0x0000000100100000,
            0x0000010100100000,
            0x0000010100100000,
        ),
        (
            0x0000000000000000,
            0x0000000080000000,
            0x0000040000000000,
            0x0000040080000000,
            0x0010000000000000,
            0x0010000080000000,
            0x0010040000000000,
            0x0010040080000000,
            0x0000000800000000,
            0x0000000880000000,
            0x0000040800000000,
            0x0000040880000000,
            0x0010000800000000,
            0x0010000880000000,
            0x0010040800000000,
            0x0010040880000000,
        ),
    )
    # ---------------------------------------------------------------
    # Subsequent key schedule rotation permutations
    # PC2ROT - PC2 inverse, then Rotate, then PC2
    # ---------------------------------------------------------------
    # NOTE: this was reordered from original table to make perm3264 logic simpler
    PC2ROTA = (
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000200000,
            0x0000000000200000,
            0x0000000000200000,
            0x0000000000200000,
            0x0000000004000000,
            0x0000000004000000,
            0x0000000004000000,
            0x0000000004000000,
            0x0000000004200000,
            0x0000000004200000,
            0x0000000004200000,
            0x0000000004200000,
        ),
        (
            0x0000000000000000,
            0x0000000000000800,
            0x0000010000000000,
            0x0000010000000800,
            0x0000000000002000,
            0x0000000000002800,
            0x0000010000002000,
            0x0000010000002800,
            0x0000000010000000,
            0x0000000010000800,
            0x0000010010000000,
            0x0000010010000800,
            0x0000000010002000,
            0x0000000010002800,
            0x0000010010002000,
            0x0000010010002800,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000100000000,
            0x0000000100000000,
            0x0000000100000000,
            0x0000000100000000,
            0x0000000000800000,
            0x0000000000800000,
            0x0000000000800000,
            0x0000000000800000,
            0x0000000100800000,
            0x0000000100800000,
            0x0000000100800000,
            0x0000000100800000,
        ),
        (
            0x0000000000000000,
            0x0000020000000000,
            0x0000000080000000,
            0x0000020080000000,
            0x0000000000400000,
            0x0000020000400000,
            0x0000000080400000,
            0x0000020080400000,
            0x0000000008000000,
            0x0000020008000000,
            0x0000000088000000,
            0x0000020088000000,
            0x0000000008400000,
            0x0000020008400000,
            0x0000000088400000,
            0x0000020088400000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000040,
            0x0000000000000040,
            0x0000000000000040,
            0x0000000000000040,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000001040,
            0x0000000000001040,
            0x0000000000001040,
            0x0000000000001040,
        ),
        (
            0x0000000000000000,
            0x0000000000000010,
            0x0000000000000400,
            0x0000000000000410,
            0x0000000000000080,
            0x0000000000000090,
            0x0000000000000480,
            0x0000000000000490,
            0x0000000040000000,
            0x0000000040000010,
            0x0000000040000400,
            0x0000000040000410,
            0x0000000040000080,
            0x0000000040000090,
            0x0000000040000480,
            0x0000000040000490,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000100000,
            0x0000000000100000,
            0x0000000000100000,
            0x0000000000100000,
            0x0000000000180000,
            0x0000000000180000,
            0x0000000000180000,
            0x0000000000180000,
        ),
        (
            0x0000000000000000,
            0x0000000000040000,
            0x0000000000000020,
            0x0000000000040020,
            0x0000000000000004,
            0x0000000000040004,
            0x0000000000000024,
            0x0000000000040024,
            0x0000000200000000,
            0x0000000200040000,
            0x0000000200000020,
            0x0000000200040020,
            0x0000000200000004,
            0x0000000200040004,
            0x0000000200000024,
            0x0000000200040024,
        ),
        (
            0x0000000000000000,
            0x0000000000000008,
            0x0000000000008000,
            0x0000000000008008,
            0x0010000000000000,
            0x0010000000000008,
            0x0010000000008000,
            0x0010000000008008,
            0x0020000000000000,
            0x0020000000000008,
            0x0020000000008000,
            0x0020000000008008,
            0x0030000000000000,
            0x0030000000000008,
            0x0030000000008000,
            0x0030000000008008,
        ),
        (
            0x0000000000000000,
            0x0000400000000000,
            0x0000080000000000,
            0x0000480000000000,
            0x0000100000000000,
            0x0000500000000000,
            0x0000180000000000,
            0x0000580000000000,
            0x4000000000000000,
            0x4000400000000000,
            0x4000080000000000,
            0x4000480000000000,
            0x4000100000000000,
            0x4000500000000000,
            0x4000180000000000,
            0x4000580000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000004000,
            0x0000000020000000,
            0x0000000020004000,
            0x0001000000000000,
            0x0001000000004000,
            0x0001000020000000,
            0x0001000020004000,
            0x0200000000000000,
            0x0200000000004000,
            0x0200000020000000,
            0x0200000020004000,
            0x0201000000000000,
            0x0201000000004000,
            0x0201000020000000,
            0x0201000020004000,
        ),
        (
            0x0000000000000000,
            0x1000000000000000,
            0x0004000000000000,
            0x1004000000000000,
            0x0002000000000000,
            0x1002000000000000,
            0x0006000000000000,
            0x1006000000000000,
            0x0000000800000000,
            0x1000000800000000,
            0x0004000800000000,
            0x1004000800000000,
            0x0002000800000000,
            0x1002000800000000,
            0x0006000800000000,
            0x1006000800000000,
        ),
        (
            0x0000000000000000,
            0x0040000000000000,
            0x2000000000000000,
            0x2040000000000000,
            0x0000008000000000,
            0x0040008000000000,
            0x2000008000000000,
            0x2040008000000000,
            0x0000001000000000,
            0x0040001000000000,
            0x2000001000000000,
            0x2040001000000000,
            0x0000009000000000,
            0x0040009000000000,
            0x2000009000000000,
            0x2040009000000000,
        ),
        (
            0x0000000000000000,
            0x0400000000000000,
            0x8000000000000000,
            0x8400000000000000,
            0x0000002000000000,
            0x0400002000000000,
            0x8000002000000000,
            0x8400002000000000,
            0x0100000000000000,
            0x0500000000000000,
            0x8100000000000000,
            0x8500000000000000,
            0x0100002000000000,
            0x0500002000000000,
            0x8100002000000000,
            0x8500002000000000,
        ),
        (
            0x0000000000000000,
            0x0000800000000000,
            0x0800000000000000,
            0x0800800000000000,
            0x0000004000000000,
            0x0000804000000000,
            0x0800004000000000,
            0x0800804000000000,
            0x0000000400000000,
            0x0000800400000000,
            0x0800000400000000,
            0x0800800400000000,
            0x0000004400000000,
            0x0000804400000000,
            0x0800004400000000,
            0x0800804400000000,
        ),
        (
            0x0000000000000000,
            0x0080000000000000,
            0x0000040000000000,
            0x0080040000000000,
            0x0008000000000000,
            0x0088000000000000,
            0x0008040000000000,
            0x0088040000000000,
            0x0000200000000000,
            0x0080200000000000,
            0x0000240000000000,
            0x0080240000000000,
            0x0008200000000000,
            0x0088200000000000,
            0x0008240000000000,
            0x0088240000000000,
        ),
    )

    # NOTE: this was reordered from original table to make perm3264 logic simpler
    PC2ROTB = (
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000400,
            0x0000000000000400,
            0x0000000000000400,
            0x0000000000000400,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080000,
            0x0000000000080400,
            0x0000000000080400,
            0x0000000000080400,
            0x0000000000080400,
        ),
        (
            0x0000000000000000,
            0x0000000000800000,
            0x0000000000004000,
            0x0000000000804000,
            0x0000000080000000,
            0x0000000080800000,
            0x0000000080004000,
            0x0000000080804000,
            0x0000000000040000,
            0x0000000000840000,
            0x0000000000044000,
            0x0000000000844000,
            0x0000000080040000,
            0x0000000080840000,
            0x0000000080044000,
            0x0000000080844000,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000008,
            0x0000000000000008,
            0x0000000000000008,
            0x0000000000000008,
            0x0000000040000000,
            0x0000000040000000,
            0x0000000040000000,
            0x0000000040000000,
            0x0000000040000008,
            0x0000000040000008,
            0x0000000040000008,
            0x0000000040000008,
        ),
        (
            0x0000000000000000,
            0x0000000020000000,
            0x0000000200000000,
            0x0000000220000000,
            0x0000000000000080,
            0x0000000020000080,
            0x0000000200000080,
            0x0000000220000080,
            0x0000000000100000,
            0x0000000020100000,
            0x0000000200100000,
            0x0000000220100000,
            0x0000000000100080,
            0x0000000020100080,
            0x0000000200100080,
            0x0000000220100080,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000002000,
            0x0000000000002000,
            0x0000000000002000,
            0x0000000000002000,
            0x0000020000000000,
            0x0000020000000000,
            0x0000020000000000,
            0x0000020000000000,
            0x0000020000002000,
            0x0000020000002000,
            0x0000020000002000,
            0x0000020000002000,
        ),
        (
            0x0000000000000000,
            0x0000000000000800,
            0x0000000100000000,
            0x0000000100000800,
            0x0000000010000000,
            0x0000000010000800,
            0x0000000110000000,
            0x0000000110000800,
            0x0000000000000004,
            0x0000000000000804,
            0x0000000100000004,
            0x0000000100000804,
            0x0000000010000004,
            0x0000000010000804,
            0x0000000110000004,
            0x0000000110000804,
        ),
        (
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000000000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000001000,
            0x0000000000000010,
            0x0000000000000010,
            0x0000000000000010,
            0x0000000000000010,
            0x0000000000001010,
            0x0000000000001010,
            0x0000000000001010,
            0x0000000000001010,
        ),
        (
            0x0000000000000000,
            0x0000000000000040,
            0x0000010000000000,
            0x0000010000000040,
            0x0000000000200000,
            0x0000000000200040,
            0x0000010000200000,
            0x0000010000200040,
            0x0000000000008000,
            0x0000000000008040,
            0x0000010000008000,
            0x0000010000008040,
            0x0000000000208000,
            0x0000000000208040,
            0x0000010000208000,
            0x0000010000208040,
        ),
        (
            0x0000000000000000,
            0x0000000004000000,
            0x0000000008000000,
            0x000000000C000000,
            0x0400000000000000,
            0x0400000004000000,
            0x0400000008000000,
            0x040000000C000000,
            0x8000000000000000,
            0x8000000004000000,
            0x8000000008000000,
            0x800000000C000000,
            0x8400000000000000,
            0x8400000004000000,
            0x8400000008000000,
            0x840000000C000000,
        ),
        (
            0x0000000000000000,
            0x0002000000000000,
            0x0200000000000000,
            0x0202000000000000,
            0x1000000000000000,
            0x1002000000000000,
            0x1200000000000000,
            0x1202000000000000,
            0x0008000000000000,
            0x000A000000000000,
            0x0208000000000000,
            0x020A000000000000,
            0x1008000000000000,
            0x100A000000000000,
            0x1208000000000000,
            0x120A000000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000400000,
            0x0000000000000020,
            0x0000000000400020,
            0x0040000000000000,
            0x0040000000400000,
            0x0040000000000020,
            0x0040000000400020,
            0x0800000000000000,
            0x0800000000400000,
            0x0800000000000020,
            0x0800000000400020,
            0x0840000000000000,
            0x0840000000400000,
            0x0840000000000020,
            0x0840000000400020,
        ),
        (
            0x0000000000000000,
            0x0080000000000000,
            0x0000008000000000,
            0x0080008000000000,
            0x2000000000000000,
            0x2080000000000000,
            0x2000008000000000,
            0x2080008000000000,
            0x0020000000000000,
            0x00A0000000000000,
            0x0020008000000000,
            0x00A0008000000000,
            0x2020000000000000,
            0x20A0000000000000,
            0x2020008000000000,
            0x20A0008000000000,
        ),
        (
            0x0000000000000000,
            0x0000002000000000,
            0x0000040000000000,
            0x0000042000000000,
            0x4000000000000000,
            0x4000002000000000,
            0x4000040000000000,
            0x4000042000000000,
            0x0000400000000000,
            0x0000402000000000,
            0x0000440000000000,
            0x0000442000000000,
            0x4000400000000000,
            0x4000402000000000,
            0x4000440000000000,
            0x4000442000000000,
        ),
        (
            0x0000000000000000,
            0x0000004000000000,
            0x0000200000000000,
            0x0000204000000000,
            0x0000080000000000,
            0x0000084000000000,
            0x0000280000000000,
            0x0000284000000000,
            0x0000800000000000,
            0x0000804000000000,
            0x0000A00000000000,
            0x0000A04000000000,
            0x0000880000000000,
            0x0000884000000000,
            0x0000A80000000000,
            0x0000A84000000000,
        ),
        (
            0x0000000000000000,
            0x0000000800000000,
            0x0000000400000000,
            0x0000000C00000000,
            0x0000100000000000,
            0x0000100800000000,
            0x0000100400000000,
            0x0000100C00000000,
            0x0010000000000000,
            0x0010000800000000,
            0x0010000400000000,
            0x0010000C00000000,
            0x0010100000000000,
            0x0010100800000000,
            0x0010100400000000,
            0x0010100C00000000,
        ),
        (
            0x0000000000000000,
            0x0100000000000000,
            0x0001000000000000,
            0x0101000000000000,
            0x0000001000000000,
            0x0100001000000000,
            0x0001001000000000,
            0x0101001000000000,
            0x0004000000000000,
            0x0104000000000000,
            0x0005000000000000,
            0x0105000000000000,
            0x0004001000000000,
            0x0104001000000000,
            0x0005001000000000,
            0x0105001000000000,
        ),
    )
    # ---------------------------------------------------------------
    # PCXROT - PC1ROT, PC2ROTA, PC2ROTB listed in order
    # of the PC1 rotation schedule, as used by des_setkey
    # ---------------------------------------------------------------
    ##ROTATES = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1)
    ##PCXROT = (
    ##        PC1ROT,  PC2ROTA, PC2ROTB, PC2ROTB,
    ##        PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTB,
    ##        PC2ROTA, PC2ROTB, PC2ROTB, PC2ROTB,
    ##        PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTA,
    ##        )

    # NOTE: modified PCXROT to contain entrys broken into pairs,
    #       to help generate them in format best used by encoder.
    PCXROT = (
        (PC1ROT, PC2ROTA),
        (PC2ROTB, PC2ROTB),
        (PC2ROTB, PC2ROTB),
        (PC2ROTB, PC2ROTB),
        (PC2ROTA, PC2ROTB),
        (PC2ROTB, PC2ROTB),
        (PC2ROTB, PC2ROTB),
        (PC2ROTB, PC2ROTA),
    )

    # ---------------------------------------------------------------
    # Bit reverse, intial permupation, expantion
    # Initial permutation/expansion table
    # ---------------------------------------------------------------
    # NOTE: this was reordered from original table to make perm3264 logic simpler
    IE3264 = (
        (
            0x0000000000000000,
            0x0000000000800800,
            0x0000000000008008,
            0x0000000000808808,
            0x0000008008000000,
            0x0000008008800800,
            0x0000008008008008,
            0x0000008008808808,
            0x0000000080080000,
            0x0000000080880800,
            0x0000000080088008,
            0x0000000080888808,
            0x0000008088080000,
            0x0000008088880800,
            0x0000008088088008,
            0x0000008088888808,
        ),
        (
            0x0000000000000000,
            0x0080080000000000,
            0x0000800800000000,
            0x0080880800000000,
            0x0800000000000080,
            0x0880080000000080,
            0x0800800800000080,
            0x0880880800000080,
            0x8008000000000000,
            0x8088080000000000,
            0x8008800800000000,
            0x8088880800000000,
            0x8808000000000080,
            0x8888080000000080,
            0x8808800800000080,
            0x8888880800000080,
        ),
        (
            0x0000000000000000,
            0x0000000000001000,
            0x0000000000000010,
            0x0000000000001010,
            0x0000000010000000,
            0x0000000010001000,
            0x0000000010000010,
            0x0000000010001010,
            0x0000000000100000,
            0x0000000000101000,
            0x0000000000100010,
            0x0000000000101010,
            0x0000000010100000,
            0x0000000010101000,
            0x0000000010100010,
            0x0000000010101010,
        ),
        (
            0x0000000000000000,
            0x0000100000000000,
            0x0000001000000000,
            0x0000101000000000,
            0x1000000000000000,
            0x1000100000000000,
            0x1000001000000000,
            0x1000101000000000,
            0x0010000000000000,
            0x0010100000000000,
            0x0010001000000000,
            0x0010101000000000,
            0x1010000000000000,
            0x1010100000000000,
            0x1010001000000000,
            0x1010101000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000002000,
            0x0000000000000020,
            0x0000000000002020,
            0x0000000020000000,
            0x0000000020002000,
            0x0000000020000020,
            0x0000000020002020,
            0x0000000000200000,
            0x0000000000202000,
            0x0000000000200020,
            0x0000000000202020,
            0x0000000020200000,
            0x0000000020202000,
            0x0000000020200020,
            0x0000000020202020,
        ),
        (
            0x0000000000000000,
            0x0000200000000000,
            0x0000002000000000,
            0x0000202000000000,
            0x2000000000000000,
            0x2000200000000000,
            0x2000002000000000,
            0x2000202000000000,
            0x0020000000000000,
            0x0020200000000000,
            0x0020002000000000,
            0x0020202000000000,
            0x2020000000000000,
            0x2020200000000000,
            0x2020002000000000,
            0x2020202000000000,
        ),
        (
            0x0000000000000000,
            0x0000000000004004,
            0x0400000000000040,
            0x0400000000004044,
            0x0000000040040000,
            0x0000000040044004,
            0x0400000040040040,
            0x0400000040044044,
            0x0000000000400400,
            0x0000000000404404,
            0x0400000000400440,
            0x0400000000404444,
            0x0000000040440400,
            0x0000000040444404,
            0x0400000040440440,
            0x0400000040444444,
        ),
        (
            0x0000000000000000,
            0x0000400400000000,
            0x0000004004000000,
            0x0000404404000000,
            0x4004000000000000,
            0x4004400400000000,
            0x4004004004000000,
            0x4004404404000000,
            0x0040040000000000,
            0x0040440400000000,
            0x0040044004000000,
            0x0040444404000000,
            0x4044040000000000,
            0x4044440400000000,
            0x4044044004000000,
            0x4044444404000000,
        ),
    )

    # ---------------------------------------------------------------
    # Table that combines the S, P, and E operations.
    # ---------------------------------------------------------------
    SPE = (
        (
            0x0080088008200000,
            0x0000008008000000,
            0x0000000000200020,
            0x0080088008200020,
            0x0000000000200000,
            0x0080088008000020,
            0x0000008008000020,
            0x0000000000200020,
            0x0080088008000020,
            0x0080088008200000,
            0x0000008008200000,
            0x0080080000000020,
            0x0080080000200020,
            0x0000000000200000,
            0x0000000000000000,
            0x0000008008000020,
            0x0000008008000000,
            0x0000000000000020,
            0x0080080000200000,
            0x0080088008000000,
            0x0080088008200020,
            0x0000008008200000,
            0x0080080000000020,
            0x0080080000200000,
            0x0000000000000020,
            0x0080080000000000,
            0x0080088008000000,
            0x0000008008200020,
            0x0080080000000000,
            0x0080080000200020,
            0x0000008008200020,
            0x0000000000000000,
            0x0000000000000000,
            0x0080088008200020,
            0x0080080000200000,
            0x0000008008000020,
            0x0080088008200000,
            0x0000008008000000,
            0x0080080000000020,
            0x0080080000200000,
            0x0000008008200020,
            0x0080080000000000,
            0x0080088008000000,
            0x0000000000200020,
            0x0080088008000020,
            0x0000000000000020,
            0x0000000000200020,
            0x0000008008200000,
            0x0080088008200020,
            0x0080088008000000,
            0x0000008008200000,
            0x0080080000200020,
            0x0000000000200000,
            0x0080080000000020,
            0x0000008008000020,
            0x0000000000000000,
            0x0000008008000000,
            0x0000000000200000,
            0x0080080000200020,
            0x0080088008200000,
            0x0000000000000020,
            0x0000008008200020,
            0x0080080000000000,
            0x0080088008000020,
        ),
        (
            0x1000800810004004,
            0x0000000000000000,
            0x0000800810000000,
            0x0000000010004004,
            0x1000000000004004,
            0x1000800800000000,
            0x0000800800004004,
            0x0000800810000000,
            0x0000800800000000,
            0x1000000010004004,
            0x1000000000000000,
            0x0000800800004004,
            0x1000000010000000,
            0x0000800810004004,
            0x0000000010004004,
            0x1000000000000000,
            0x0000000010000000,
            0x1000800800004004,
            0x1000000010004004,
            0x0000800800000000,
            0x1000800810000000,
            0x0000000000004004,
            0x0000000000000000,
            0x1000000010000000,
            0x1000800800004004,
            0x1000800810000000,
            0x0000800810004004,
            0x1000000000004004,
            0x0000000000004004,
            0x0000000010000000,
            0x1000800800000000,
            0x1000800810004004,
            0x1000000010000000,
            0x0000800810004004,
            0x0000800800004004,
            0x1000800810000000,
            0x1000800810004004,
            0x1000000010000000,
            0x1000000000004004,
            0x0000000000000000,
            0x0000000000004004,
            0x1000800800000000,
            0x0000000010000000,
            0x1000000010004004,
            0x0000800800000000,
            0x0000000000004004,
            0x1000800810000000,
            0x1000800800004004,
            0x0000800810004004,
            0x0000800800000000,
            0x0000000000000000,
            0x1000000000004004,
            0x1000000000000000,
            0x1000800810004004,
            0x0000800810000000,
            0x0000000010004004,
            0x1000000010004004,
            0x0000000010000000,
            0x1000800800000000,
            0x0000800800004004,
            0x1000800800004004,
            0x1000000000000000,
            0x0000000010004004,
            0x0000800810000000,
        ),
        (
            0x0000000000400410,
            0x0010004004400400,
            0x0010000000000000,
            0x0010000000400410,
            0x0000004004000010,
            0x0000000000400400,
            0x0010000000400410,
            0x0010004004000000,
            0x0010000000400400,
            0x0000004004000000,
            0x0000004004400400,
            0x0000000000000010,
            0x0010004004400410,
            0x0010000000000010,
            0x0000000000000010,
            0x0000004004400410,
            0x0000000000000000,
            0x0000004004000010,
            0x0010004004400400,
            0x0010000000000000,
            0x0010000000000010,
            0x0010004004400410,
            0x0000004004000000,
            0x0000000000400410,
            0x0000004004400410,
            0x0010000000400400,
            0x0010004004000010,
            0x0000004004400400,
            0x0010004004000000,
            0x0000000000000000,
            0x0000000000400400,
            0x0010004004000010,
            0x0010004004400400,
            0x0010000000000000,
            0x0000000000000010,
            0x0000004004000000,
            0x0010000000000010,
            0x0000004004000010,
            0x0000004004400400,
            0x0010000000400410,
            0x0000000000000000,
            0x0010004004400400,
            0x0010004004000000,
            0x0000004004400410,
            0x0000004004000010,
            0x0000000000400400,
            0x0010004004400410,
            0x0000000000000010,
            0x0010004004000010,
            0x0000000000400410,
            0x0000000000400400,
            0x0010004004400410,
            0x0000004004000000,
            0x0010000000400400,
            0x0010000000400410,
            0x0010004004000000,
            0x0010000000400400,
            0x0000000000000000,
            0x0000004004400410,
            0x0010000000000010,
            0x0000000000400410,
            0x0010004004000010,
            0x0010000000000000,
            0x0000004004400400,
        ),
        (
            0x0800100040040080,
            0x0000100000001000,
            0x0800000000000080,
            0x0800100040041080,
            0x0000000000000000,
            0x0000000040041000,
            0x0800100000001080,
            0x0800000040040080,
            0x0000100040041000,
            0x0800000000001080,
            0x0000000000001000,
            0x0800100000000080,
            0x0800000000001080,
            0x0800100040040080,
            0x0000000040040000,
            0x0000000000001000,
            0x0800000040041080,
            0x0000100040040000,
            0x0000100000000000,
            0x0800000000000080,
            0x0000100040040000,
            0x0800100000001080,
            0x0000000040041000,
            0x0000100000000000,
            0x0800100000000080,
            0x0000000000000000,
            0x0800000040040080,
            0x0000100040041000,
            0x0000100000001000,
            0x0800000040041080,
            0x0800100040041080,
            0x0000000040040000,
            0x0800000040041080,
            0x0800100000000080,
            0x0000000040040000,
            0x0800000000001080,
            0x0000100040040000,
            0x0000100000001000,
            0x0800000000000080,
            0x0000000040041000,
            0x0800100000001080,
            0x0000000000000000,
            0x0000100000000000,
            0x0800000040040080,
            0x0000000000000000,
            0x0800000040041080,
            0x0000100040041000,
            0x0000100000000000,
            0x0000000000001000,
            0x0800100040041080,
            0x0800100040040080,
            0x0000000040040000,
            0x0800100040041080,
            0x0800000000000080,
            0x0000100000001000,
            0x0800100040040080,
            0x0800000040040080,
            0x0000100040040000,
            0x0000000040041000,
            0x0800100000001080,
            0x0800100000000080,
            0x0000000000001000,
            0x0800000000001080,
            0x0000100040041000,
        ),
        (
            0x0000000000800800,
            0x0000001000000000,
            0x0040040000000000,
            0x2040041000800800,
            0x2000001000800800,
            0x0040040000800800,
            0x2040041000000000,
            0x0000001000800800,
            0x0000001000000000,
            0x2000000000000000,
            0x2000000000800800,
            0x0040041000000000,
            0x2040040000800800,
            0x2000001000800800,
            0x0040041000800800,
            0x0000000000000000,
            0x0040041000000000,
            0x0000000000800800,
            0x2000001000000000,
            0x2040040000000000,
            0x0040040000800800,
            0x2040041000000000,
            0x0000000000000000,
            0x2000000000800800,
            0x2000000000000000,
            0x2040040000800800,
            0x2040041000800800,
            0x2000001000000000,
            0x0000001000800800,
            0x0040040000000000,
            0x2040040000000000,
            0x0040041000800800,
            0x0040041000800800,
            0x2040040000800800,
            0x2000001000000000,
            0x0000001000800800,
            0x0000001000000000,
            0x2000000000000000,
            0x2000000000800800,
            0x0040040000800800,
            0x0000000000800800,
            0x0040041000000000,
            0x2040041000800800,
            0x0000000000000000,
            0x2040041000000000,
            0x0000000000800800,
            0x0040040000000000,
            0x2000001000000000,
            0x2040040000800800,
            0x0040040000000000,
            0x0000000000000000,
            0x2040041000800800,
            0x2000001000800800,
            0x0040041000800800,
            0x2040040000000000,
            0x0000001000000000,
            0x0040041000000000,
            0x2000001000800800,
            0x0040040000800800,
            0x2040040000000000,
            0x2000000000000000,
            0x2040041000000000,
            0x0000001000800800,
            0x2000000000800800,
        ),
        (
            0x4004000000008008,
            0x4004000020000000,
            0x0000000000000000,
            0x0000200020008008,
            0x4004000020000000,
            0x0000200000000000,
            0x4004200000008008,
            0x0000000020000000,
            0x4004200000000000,
            0x4004200020008008,
            0x0000200020000000,
            0x0000000000008008,
            0x0000200000008008,
            0x4004000000008008,
            0x0000000020008008,
            0x4004200020000000,
            0x0000000020000000,
            0x4004200000008008,
            0x4004000020008008,
            0x0000000000000000,
            0x0000200000000000,
            0x4004000000000000,
            0x0000200020008008,
            0x4004000020008008,
            0x4004200020008008,
            0x0000000020008008,
            0x0000000000008008,
            0x4004200000000000,
            0x4004000000000000,
            0x0000200020000000,
            0x4004200020000000,
            0x0000200000008008,
            0x4004200000000000,
            0x0000000000008008,
            0x0000200000008008,
            0x4004200020000000,
            0x0000200020008008,
            0x4004000020000000,
            0x0000000000000000,
            0x0000200000008008,
            0x0000000000008008,
            0x0000200000000000,
            0x4004000020008008,
            0x0000000020000000,
            0x4004000020000000,
            0x4004200020008008,
            0x0000200020000000,
            0x4004000000000000,
            0x4004200020008008,
            0x0000200020000000,
            0x0000000020000000,
            0x4004200000008008,
            0x4004000000008008,
            0x0000000020008008,
            0x4004200020000000,
            0x0000000000000000,
            0x0000200000000000,
            0x4004000000008008,
            0x4004200000008008,
            0x0000200020008008,
            0x0000000020008008,
            0x4004200000000000,
            0x4004000000000000,
            0x4004000020008008,
        ),
        (
            0x0000400400000000,
            0x0020000000000000,
            0x0020000000100000,
            0x0400000000100040,
            0x0420400400100040,
            0x0400400400000040,
            0x0020400400000000,
            0x0000000000000000,
            0x0000000000100000,
            0x0420000000100040,
            0x0420000000000040,
            0x0000400400100000,
            0x0400000000000040,
            0x0020400400100000,
            0x0000400400100000,
            0x0420000000000040,
            0x0420000000100040,
            0x0000400400000000,
            0x0400400400000040,
            0x0420400400100040,
            0x0000000000000000,
            0x0020000000100000,
            0x0400000000100040,
            0x0020400400000000,
            0x0400400400100040,
            0x0420400400000040,
            0x0020400400100000,
            0x0400000000000040,
            0x0420400400000040,
            0x0400400400100040,
            0x0020000000000000,
            0x0000000000100000,
            0x0420400400000040,
            0x0000400400100000,
            0x0400400400100040,
            0x0420000000000040,
            0x0000400400000000,
            0x0020000000000000,
            0x0000000000100000,
            0x0400400400100040,
            0x0420000000100040,
            0x0420400400000040,
            0x0020400400000000,
            0x0000000000000000,
            0x0020000000000000,
            0x0400000000100040,
            0x0400000000000040,
            0x0020000000100000,
            0x0000000000000000,
            0x0420000000100040,
            0x0020000000100000,
            0x0020400400000000,
            0x0420000000000040,
            0x0000400400000000,
            0x0420400400100040,
            0x0000000000100000,
            0x0020400400100000,
            0x0400000000000040,
            0x0400400400000040,
            0x0420400400100040,
            0x0400000000100040,
            0x0020400400100000,
            0x0000400400100000,
            0x0400400400000040,
        ),
        (
            0x8008000080082000,
            0x0000002080082000,
            0x8008002000000000,
            0x0000000000000000,
            0x0000002000002000,
            0x8008000080080000,
            0x0000000080082000,
            0x8008002080082000,
            0x8008000000000000,
            0x0000000000002000,
            0x0000002080080000,
            0x8008002000000000,
            0x8008002080080000,
            0x8008002000002000,
            0x8008000000002000,
            0x0000000080082000,
            0x0000002000000000,
            0x8008002080080000,
            0x8008000080080000,
            0x0000002000002000,
            0x8008002080082000,
            0x8008000000002000,
            0x0000000000000000,
            0x0000002080080000,
            0x0000000000002000,
            0x0000000080080000,
            0x8008002000002000,
            0x8008000080082000,
            0x0000000080080000,
            0x0000002000000000,
            0x0000002080082000,
            0x8008000000000000,
            0x0000000080080000,
            0x0000002000000000,
            0x8008000000002000,
            0x8008002080082000,
            0x8008002000000000,
            0x0000000000002000,
            0x0000000000000000,
            0x0000002080080000,
            0x8008000080082000,
            0x8008002000002000,
            0x0000002000002000,
            0x8008000080080000,
            0x0000002080082000,
            0x8008000000000000,
            0x8008000080080000,
            0x0000002000002000,
            0x8008002080082000,
            0x0000000080080000,
            0x0000000080082000,
            0x8008000000002000,
            0x0000002080080000,
            0x8008002000000000,
            0x8008002000002000,
            0x0000000080082000,
            0x8008000000000000,
            0x0000002080082000,
            0x8008002080080000,
            0x0000000000000000,
            0x0000000000002000,
            0x8008000080082000,
            0x0000002000000000,
            0x8008002080080000,
        ),
    )

    # ---------------------------------------------------------------
    # compressed/interleaved => final permutation table
    # Compression, final permutation, bit reverse
    # ---------------------------------------------------------------
    # NOTE: this was reordered from original table to make perm6464 logic simpler
    CF6464 = (
        (
            0x0000000000000000,
            0x0000002000000000,
            0x0000200000000000,
            0x0000202000000000,
            0x0020000000000000,
            0x0020002000000000,
            0x0020200000000000,
            0x0020202000000000,
            0x2000000000000000,
            0x2000002000000000,
            0x2000200000000000,
            0x2000202000000000,
            0x2020000000000000,
            0x2020002000000000,
            0x2020200000000000,
            0x2020202000000000,
        ),
        (
            0x0000000000000000,
            0x0000000200000000,
            0x0000020000000000,
            0x0000020200000000,
            0x0002000000000000,
            0x0002000200000000,
            0x0002020000000000,
            0x0002020200000000,
            0x0200000000000000,
            0x0200000200000000,
            0x0200020000000000,
            0x0200020200000000,
            0x0202000000000000,
            0x0202000200000000,
            0x0202020000000000,
            0x0202020200000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000020,
            0x0000000000002000,
            0x0000000000002020,
            0x0000000000200000,
            0x0000000000200020,
            0x0000000000202000,
            0x0000000000202020,
            0x0000000020000000,
            0x0000000020000020,
            0x0000000020002000,
            0x0000000020002020,
            0x0000000020200000,
            0x0000000020200020,
            0x0000000020202000,
            0x0000000020202020,
        ),
        (
            0x0000000000000000,
            0x0000000000000002,
            0x0000000000000200,
            0x0000000000000202,
            0x0000000000020000,
            0x0000000000020002,
            0x0000000000020200,
            0x0000000000020202,
            0x0000000002000000,
            0x0000000002000002,
            0x0000000002000200,
            0x0000000002000202,
            0x0000000002020000,
            0x0000000002020002,
            0x0000000002020200,
            0x0000000002020202,
        ),
        (
            0x0000000000000000,
            0x0000008000000000,
            0x0000800000000000,
            0x0000808000000000,
            0x0080000000000000,
            0x0080008000000000,
            0x0080800000000000,
            0x0080808000000000,
            0x8000000000000000,
            0x8000008000000000,
            0x8000800000000000,
            0x8000808000000000,
            0x8080000000000000,
            0x8080008000000000,
            0x8080800000000000,
            0x8080808000000000,
        ),
        (
            0x0000000000000000,
            0x0000000800000000,
            0x0000080000000000,
            0x0000080800000000,
            0x0008000000000000,
            0x0008000800000000,
            0x0008080000000000,
            0x0008080800000000,
            0x0800000000000000,
            0x0800000800000000,
            0x0800080000000000,
            0x0800080800000000,
            0x0808000000000000,
            0x0808000800000000,
            0x0808080000000000,
            0x0808080800000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000080,
            0x0000000000008000,
            0x0000000000008080,
            0x0000000000800000,
            0x0000000000800080,
            0x0000000000808000,
            0x0000000000808080,
            0x0000000080000000,
            0x0000000080000080,
            0x0000000080008000,
            0x0000000080008080,
            0x0000000080800000,
            0x0000000080800080,
            0x0000000080808000,
            0x0000000080808080,
        ),
        (
            0x0000000000000000,
            0x0000000000000008,
            0x0000000000000800,
            0x0000000000000808,
            0x0000000000080000,
            0x0000000000080008,
            0x0000000000080800,
            0x0000000000080808,
            0x0000000008000000,
            0x0000000008000008,
            0x0000000008000800,
            0x0000000008000808,
            0x0000000008080000,
            0x0000000008080008,
            0x0000000008080800,
            0x0000000008080808,
        ),
        (
            0x0000000000000000,
            0x0000001000000000,
            0x0000100000000000,
            0x0000101000000000,
            0x0010000000000000,
            0x0010001000000000,
            0x0010100000000000,
            0x0010101000000000,
            0x1000000000000000,
            0x1000001000000000,
            0x1000100000000000,
            0x1000101000000000,
            0x1010000000000000,
            0x1010001000000000,
            0x1010100000000000,
            0x1010101000000000,
        ),
        (
            0x0000000000000000,
            0x0000000100000000,
            0x0000010000000000,
            0x0000010100000000,
            0x0001000000000000,
            0x0001000100000000,
            0x0001010000000000,
            0x0001010100000000,
            0x0100000000000000,
            0x0100000100000000,
            0x0100010000000000,
            0x0100010100000000,
            0x0101000000000000,
            0x0101000100000000,
            0x0101010000000000,
            0x0101010100000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000010,
            0x0000000000001000,
            0x0000000000001010,
            0x0000000000100000,
            0x0000000000100010,
            0x0000000000101000,
            0x0000000000101010,
            0x0000000010000000,
            0x0000000010000010,
            0x0000000010001000,
            0x0000000010001010,
            0x0000000010100000,
            0x0000000010100010,
            0x0000000010101000,
            0x0000000010101010,
        ),
        (
            0x0000000000000000,
            0x0000000000000001,
            0x0000000000000100,
            0x0000000000000101,
            0x0000000000010000,
            0x0000000000010001,
            0x0000000000010100,
            0x0000000000010101,
            0x0000000001000000,
            0x0000000001000001,
            0x0000000001000100,
            0x0000000001000101,
            0x0000000001010000,
            0x0000000001010001,
            0x0000000001010100,
            0x0000000001010101,
        ),
        (
            0x0000000000000000,
            0x0000004000000000,
            0x0000400000000000,
            0x0000404000000000,
            0x0040000000000000,
            0x0040004000000000,
            0x0040400000000000,
            0x0040404000000000,
            0x4000000000000000,
            0x4000004000000000,
            0x4000400000000000,
            0x4000404000000000,
            0x4040000000000000,
            0x4040004000000000,
            0x4040400000000000,
            0x4040404000000000,
        ),
        (
            0x0000000000000000,
            0x0000000400000000,
            0x0000040000000000,
            0x0000040400000000,
            0x0004000000000000,
            0x0004000400000000,
            0x0004040000000000,
            0x0004040400000000,
            0x0400000000000000,
            0x0400000400000000,
            0x0400040000000000,
            0x0400040400000000,
            0x0404000000000000,
            0x0404000400000000,
            0x0404040000000000,
            0x0404040400000000,
        ),
        (
            0x0000000000000000,
            0x0000000000000040,
            0x0000000000004000,
            0x0000000000004040,
            0x0000000000400000,
            0x0000000000400040,
            0x0000000000404000,
            0x0000000000404040,
            0x0000000040000000,
            0x0000000040000040,
            0x0000000040004000,
            0x0000000040004040,
            0x0000000040400000,
            0x0000000040400040,
            0x0000000040404000,
            0x0000000040404040,
        ),
        (
            0x0000000000000000,
            0x0000000000000004,
            0x0000000000000400,
            0x0000000000000404,
            0x0000000000040000,
            0x0000000000040004,
            0x0000000000040400,
            0x0000000000040404,
            0x0000000004000000,
            0x0000000004000004,
            0x0000000004000400,
            0x0000000004000404,
            0x0000000004040000,
            0x0000000004040004,
            0x0000000004040400,
            0x0000000004040404,
        ),
    )


def _permute(c, p):
    """Returns the permutation of the given 32-bit or 64-bit code with
    the specified permutation table."""
    # NOTE: only difference between 32 & 64 bit permutations
    #       is that len(p)==8 for 32 bit, and len(p)==16 for 64 bit.
    out = 0
    for r in p:
        out |= r[c & 0xF]
        c >>= 4
    return out


# FIXME: more properly named _uint8_struct...
_uint64_struct = struct.Struct(">Q")


def _pack64(value):
    return _uint64_struct.pack(value)


def _unpack64(value):
    return _uint64_struct.unpack(value)[0]


def _pack56(value):
    return _uint64_struct.pack(value)[1:]


def _unpack56(value):
    return _uint64_struct.unpack(b"\x00" + value)[0]


##def expand_7bit(value):
##    "expand 7-bit integer => 7-bits + 1 odd-parity bit"
##    # parity calc adapted from 32-bit even parity alg found at
##    # http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
##    assert 0 <= value < 0x80, "value out of range"
##    return (value<<1) | (0x9669 >> ((value ^ (value >> 4)) & 0xf)) & 1

_EXPAND_ITER = range(49, -7, -7)


def expand_des_key(key):
    """convert DES from 7 bytes to 8 bytes (by inserting empty parity bits)"""
    if isinstance(key, bytes):
        if len(key) != 7:
            raise ValueError("key must be 7 bytes in size")
    elif isinstance(key, int):
        if key < 0 or key > INT_56_MASK:
            raise ValueError("key must be 56-bit non-negative integer")
        return _unpack64(expand_des_key(_pack56(key)))
    else:
        raise exc.ExpectedTypeError(key, "bytes or int", "key")
    key = _unpack56(key)
    # NOTE: the following would insert correctly-valued parity bits in each key,
    # but the parity bit would just be ignored in des_encrypt_block(),
    # so not bothering to use it.
    # XXX: could make parity-restoring optionally available via flag
    ##return bytes(expand_7bit((key >> shift) & 0x7f) for shift in _EXPAND_ITER)
    return bytes(((key >> shift) & 0x7F) << 1 for shift in _EXPAND_ITER)


def shrink_des_key(key):
    """convert DES key from 8 bytes to 7 bytes (by discarding the parity bits)"""
    if isinstance(key, bytes):
        if len(key) != 8:
            raise ValueError("key must be 8 bytes in size")
        return _pack56(shrink_des_key(_unpack64(key)))
    if isinstance(key, int):
        if key < 0 or key > INT_64_MASK:
            raise ValueError("key must be 64-bit non-negative integer")
    else:
        raise exc.ExpectedTypeError(key, "bytes or int", "key")
    key >>= 1
    result = 0
    offset = 0
    while offset < 56:
        result |= (key & 0x7F) << offset
        key >>= 8
        offset += 7
    assert not (result & ~INT_64_MASK)
    return result


def des_encrypt_block(key, input, salt=0, rounds=1):
    """encrypt single block of data using DES, operates on 8-byte strings.

    :arg key:
        DES key as 7 byte string, or 8 byte string with parity bits
        (parity bit values are ignored).

    :arg input:
        plaintext block to encrypt, as 8 byte string.

    :arg salt:
        Optional 24-bit integer used to mutate the base DES algorithm in a
        manner specific to :class:`~passlib.hash.des_crypt` and its variants.
        The default value ``0`` provides the normal (unsalted) DES behavior.
        The salt functions as follows:
        if the ``i``'th bit of ``salt`` is set,
        bits ``i`` and ``i+24`` are swapped in the DES E-box output.

    :arg rounds:
        Optional number of rounds of to apply the DES key schedule.
        the default (``rounds=1``) provides the normal DES behavior,
        but :class:`~passlib.hash.des_crypt` and its variants use
        alternate rounds values.

    :raises TypeError: if any of the provided args are of the wrong type.
    :raises ValueError:
        if any of the input blocks are the wrong size,
        or the salt/rounds values are out of range.

    :returns:
        resulting 8-byte ciphertext block.
    """
    # validate & unpack key
    if isinstance(key, bytes):
        if len(key) == 7:
            key = expand_des_key(key)
        elif len(key) != 8:
            raise ValueError("key must be 7 or 8 bytes")
        key = _unpack64(key)
    else:
        raise exc.ExpectedTypeError(key, "bytes", "key")

    # validate & unpack input
    if isinstance(input, bytes):
        if len(input) != 8:
            raise ValueError("input block must be 8 bytes")
        input = _unpack64(input)
    else:
        raise exc.ExpectedTypeError(input, "bytes", "input")

    # hand things off to other func
    result = des_encrypt_int_block(key, input, salt, rounds)

    # repack result
    return _pack64(result)


def des_encrypt_int_block(key, input, salt=0, rounds=1):
    """encrypt single block of data using DES, operates on 64-bit integers.

    this function is essentially the same as :func:`des_encrypt_block`,
    except that it operates on integers, and will NOT automatically
    expand 56-bit keys if provided (since there's no way to detect them).

    :arg key:
        DES key as 64-bit integer (the parity bits are ignored).

    :arg input:
        input block as 64-bit integer

    :arg salt:
        optional 24-bit integer used to mutate the base DES algorithm.
        defaults to ``0`` (no mutation applied).

    :arg rounds:
        optional number of rounds of to apply the DES key schedule.
        defaults to ``1``.

    :raises TypeError: if any of the provided args are of the wrong type.
    :raises ValueError:
        if any of the input blocks are the wrong size,
        or the salt/rounds values are out of range.

    :returns:
        resulting ciphertext as 64-bit integer.
    """
    # ---------------------------------------------------------------
    # input validation
    # ---------------------------------------------------------------

    # validate salt, rounds
    if rounds < 1:
        raise ValueError("rounds must be positive integer")
    if salt < 0 or salt > INT_24_MASK:
        raise ValueError("salt must be 24-bit non-negative integer")

    # validate & unpack key
    if not isinstance(key, int):
        raise exc.ExpectedTypeError(key, "int", "key")
    if key < 0 or key > INT_64_MASK:
        raise ValueError("key must be 64-bit non-negative integer")

    # validate & unpack input
    if not isinstance(input, int):
        raise exc.ExpectedTypeError(input, "int", "input")
    if input < 0 or input > INT_64_MASK:
        raise ValueError("input must be 64-bit non-negative integer")

    # ---------------------------------------------------------------
    # DES setup
    # ---------------------------------------------------------------
    # load tables if not already done
    if PCXROT is None:
        _load_tables()

    # load SPE into local vars to speed things up and remove an array access call
    SPE0, SPE1, SPE2, SPE3, SPE4, SPE5, SPE6, SPE7 = SPE

    # NOTE: parity bits are ignored completely
    # (UTs do fuzz testing to ensure this)

    # generate key schedule
    # NOTE: generation was modified to output two elements at a time,
    # so that per-round loop could do two passes at once.
    def _iter_key_schedule(ks_odd):
        """given 64-bit key, iterates over the 8 (even,odd) key schedule pairs"""
        for p_even, p_odd in PCXROT:
            ks_even = _permute(ks_odd, p_even)
            ks_odd = _permute(ks_even, p_odd)
            yield ks_even & _KS_MASK, ks_odd & _KS_MASK

    ks_list = list(_iter_key_schedule(key))

    # expand 24 bit salt -> 32 bit per des_crypt & bsdi_crypt
    salt = (
        ((salt & 0x00003F) << 26)
        | ((salt & 0x000FC0) << 12)
        | ((salt & 0x03F000) >> 2)
        | ((salt & 0xFC0000) >> 16)
    )

    # init L & R
    if input == 0:
        L = R = 0
    else:
        L = ((input >> 31) & 0xAAAAAAAA) | (input & 0x55555555)
        L = _permute(L, IE3264)

        R = ((input >> 32) & 0xAAAAAAAA) | ((input >> 1) & 0x55555555)
        R = _permute(R, IE3264)

    # ---------------------------------------------------------------
    # main DES loop - run for specified number of rounds
    # ---------------------------------------------------------------
    while rounds:
        rounds -= 1

        # run over each part of the schedule, 2 parts at a time
        for ks_even, ks_odd in ks_list:
            k = ((R >> 32) ^ R) & salt  # use the salt to flip specific bits
            B = (k << 32) ^ k ^ R ^ ks_even

            L ^= (
                SPE0[(B >> 58) & 0x3F]
                ^ SPE1[(B >> 50) & 0x3F]
                ^ SPE2[(B >> 42) & 0x3F]
                ^ SPE3[(B >> 34) & 0x3F]
                ^ SPE4[(B >> 26) & 0x3F]
                ^ SPE5[(B >> 18) & 0x3F]
                ^ SPE6[(B >> 10) & 0x3F]
                ^ SPE7[(B >> 2) & 0x3F]
            )

            k = ((L >> 32) ^ L) & salt  # use the salt to flip specific bits
            B = (k << 32) ^ k ^ L ^ ks_odd

            R ^= (
                SPE0[(B >> 58) & 0x3F]
                ^ SPE1[(B >> 50) & 0x3F]
                ^ SPE2[(B >> 42) & 0x3F]
                ^ SPE3[(B >> 34) & 0x3F]
                ^ SPE4[(B >> 26) & 0x3F]
                ^ SPE5[(B >> 18) & 0x3F]
                ^ SPE6[(B >> 10) & 0x3F]
                ^ SPE7[(B >> 2) & 0x3F]
            )

        # swap L and R
        L, R = R, L

    # ---------------------------------------------------------------
    # return final result
    # ---------------------------------------------------------------
    C = (
        ((L >> 3) & 0x0F0F0F0F00000000)
        | ((L << 33) & 0xF0F0F0F000000000)
        | ((R >> 35) & 0x000000000F0F0F0F)
        | ((R << 1) & 0x00000000F0F0F0F0)
    )
    return _permute(C, CF6464)
