/**
 * A group of prime order, C++ wrapper.
 * 
 * The Decaf library implements cryptographic operations on a an elliptic curve
 * group of prime order. It accomplishes this by using a twisted Edwards
 * curve (isogenous to $(iso_to)) and wiping out the cofactor.
 * 
 * Most of the functions in this file run in constant time, can't fail
 * except for ubiquitous reasons like memory exhaustion, and contain no
 * data-dependend branches, timing or memory accesses.  There are some
 * exceptions, which should be noted.  Typically, decoding functions can
 * fail.
 */

/** This code uses posix_memalign. */
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#include <stdlib.h>
#include <string.h> /* for memcpy */

#include <decaf/point_$(gf_bits).h>
#include <decaf/ed$(gf_bits).h>
#include <decaf/secure_buffer.hxx>
#include <string>
#include <sys/types.h>
#include <limits.h>

/** @cond internal */
#if __cplusplus >= 201103L
#define DECAF_NOEXCEPT noexcept
#else
#define DECAF_NOEXCEPT throw()
#endif
/** @endcond */

namespace decaf {

/**
 * $(iso_to)/Decaf instantiation of group.
 */
struct $(cxx_ns) {

/** The name of the curve */
static inline const char *name() { return "$(name)"; }

/** The name of the curve */
static inline int bits() { return $(gf_bits); }

/** The curve's cofactor (removed, but useful for testing) */
static const int REMOVED_COFACTOR = $(cofactor);

/** Residue class of field modulus: p == this mod 2*(this-1) */
static const int FIELD_MODULUS_TYPE = $(modulus &~ (modulus-3));

/** @cond internal */
class Point;
class Precomputed;
/** @endcond */

/**
 * A scalar modulo the curve order.
 * Supports the usual arithmetic operations, all in constant time.
 */
class Scalar : public Serializable<Scalar> {
public:
    /** wrapped C type */
    typedef $(c_ns)_scalar_t Wrapped;
    
    /** Size of a serialized element */
    static const size_t SER_BYTES = $(C_NS)_SCALAR_BYTES;

    /** access to the underlying scalar object */
    Wrapped s;

    /** @cond internal */
    /** Don't initialize. */
    inline Scalar(const NOINIT &) DECAF_NOEXCEPT {}
    /** @endcond */

    /** Set to an unsigned word */
    inline Scalar(uint64_t w) DECAF_NOEXCEPT { *this = w; }

    /** Set to a signed word */
    inline Scalar(int64_t w) DECAF_NOEXCEPT { *this = w; }

    /** Set to an unsigned word */
    inline Scalar(unsigned int w) DECAF_NOEXCEPT { *this = w; }

    /** Set to a signed word */
    inline Scalar(int w) DECAF_NOEXCEPT { *this = w; }

    /** Construct from RNG */
    inline explicit Scalar(Rng &rng) DECAF_NOEXCEPT {
        FixedArrayBuffer<SER_BYTES + 16> sb(rng);
        *this = sb;
    }

    /** Construct from decaf_scalar_t object. */
    inline Scalar(const Wrapped &t = $(c_ns)_scalar_zero) DECAF_NOEXCEPT { $(c_ns)_scalar_copy(s,t); }

    /** Copy constructor. */
    inline Scalar(const Scalar &x) DECAF_NOEXCEPT { *this = x; }

    /** Construct from arbitrary-length little-endian byte sequence. */
    inline Scalar(const Block &buffer) DECAF_NOEXCEPT { *this = buffer; }

    /** Serializable instance */
    inline size_t ser_size() const DECAF_NOEXCEPT { return SER_BYTES; }

    /** Serializable instance */
    inline void serialize_into(unsigned char *buffer) const DECAF_NOEXCEPT {
        $(c_ns)_scalar_encode(buffer, s);
    }

    /** Assignment. */
    inline Scalar& operator=(const Scalar &x) DECAF_NOEXCEPT { $(c_ns)_scalar_copy(s,x.s); return *this; }

    /** Assign from unsigned 64-bit integer. */
    inline Scalar& operator=(uint64_t w) DECAF_NOEXCEPT { $(c_ns)_scalar_set_unsigned(s,w); return *this; }


    /** Assign from signed int. */
    inline Scalar& operator=(int64_t w) DECAF_NOEXCEPT {
#ifdef _MSC_VER
#pragma warning ( push)
#pragma warning ( disable : 4146)
#endif
        Scalar t(-(uint64_t)INT_MIN);
#ifdef _MSC_VER
#pragma warning ( pop)
#endif
        $(c_ns)_scalar_set_unsigned(s,(uint64_t)w - (uint64_t)INT_MIN);
        *this -= t;
        return *this;
    }

    /** Assign from unsigned int. */
    inline Scalar& operator=(unsigned int w) DECAF_NOEXCEPT { return *this = (uint64_t)w; }

    /** Assign from signed int. */
    inline Scalar& operator=(int w) DECAF_NOEXCEPT { return *this = (int64_t)w; }

    /** Destructor securely zeorizes the scalar. */
    inline ~Scalar() DECAF_NOEXCEPT { $(c_ns)_scalar_destroy(s); }

    /** Assign from arbitrary-length little-endian byte sequence in a Block. */
    inline Scalar &operator=(const Block &bl) DECAF_NOEXCEPT {
        $(c_ns)_scalar_decode_long(s,bl.data(),bl.size()); return *this;
    }

    /**
     * Decode from correct-length little-endian byte sequence.
     * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q.
     */
    static inline decaf_error_t DECAF_WARN_UNUSED decode (
        Scalar &sc, const FixedBlock<SER_BYTES> buffer
    ) DECAF_NOEXCEPT {
        return $(c_ns)_scalar_decode(sc.s,buffer.data());
    }

    /** Add. */
    inline Scalar operator+ (const Scalar &q) const DECAF_NOEXCEPT { Scalar r((NOINIT())); $(c_ns)_scalar_add(r.s,s,q.s); return r; }

    /** Add to this. */
    inline Scalar &operator+=(const Scalar &q) DECAF_NOEXCEPT { $(c_ns)_scalar_add(s,s,q.s); return *this; }

    /** Subtract. */
    inline Scalar operator- (const Scalar &q) const DECAF_NOEXCEPT { Scalar r((NOINIT())); $(c_ns)_scalar_sub(r.s,s,q.s); return r; }

    /** Subtract from this. */
    inline Scalar &operator-=(const Scalar &q) DECAF_NOEXCEPT { $(c_ns)_scalar_sub(s,s,q.s); return *this; }

    /** Multiply */
    inline Scalar operator* (const Scalar &q) const DECAF_NOEXCEPT { Scalar r((NOINIT())); $(c_ns)_scalar_mul(r.s,s,q.s); return r; }

    /** Multiply into this. */
    inline Scalar &operator*=(const Scalar &q) DECAF_NOEXCEPT { $(c_ns)_scalar_mul(s,s,q.s); return *this; }

    /** Negate */
    inline Scalar operator- () const DECAF_NOEXCEPT { Scalar r((NOINIT())); $(c_ns)_scalar_sub(r.s,$(c_ns)_scalar_zero,s); return r; }

    /** Return 1/this.
     * @throw CryptoException if this is 0.
     */
    inline Scalar inverse() const /*throw(CryptoException)*/ {
        Scalar r;
        if (DECAF_SUCCESS != $(c_ns)_scalar_invert(r.s,s)) {
            throw CryptoException();
        }
        return r;
    }

    /** Invert with Fermat's Little Theorem (slow!). If *this == 0, set r=0
     * and return DECAF_FAILURE. */
    inline decaf_error_t DECAF_WARN_UNUSED
    inverse_noexcept(Scalar &r) const DECAF_NOEXCEPT {
        return $(c_ns)_scalar_invert(r.s,s);
    }

    /** Return this/q. @throw CryptoException if q == 0. */
    inline Scalar operator/ (const Scalar &q) const /*throw(CryptoException)*/ { return *this * q.inverse(); }

    /** Set this to this/q. @throw CryptoException if q == 0. */
    inline Scalar &operator/=(const Scalar &q) /*throw(CryptoException)*/ { return *this *= q.inverse(); }

    /** Return half this scalar.  Much faster than /2. */
    inline Scalar half() const { Scalar out; $(c_ns)_scalar_halve(out.s,s); return out; }

    /** Compare in constant time */
    inline bool operator!=(const Scalar &q) const DECAF_NOEXCEPT { return !(*this == q); }

    /** Compare in constant time */
    inline bool operator==(const Scalar &q) const DECAF_NOEXCEPT { return !!$(c_ns)_scalar_eq(s,q.s); }

    /** Scalarmul with scalar on left. */
    inline Point operator* (const Point &q) const DECAF_NOEXCEPT { return q * (*this); }

    /** Scalarmul-precomputed with scalar on left. */
    inline Point operator* (const Precomputed &q) const DECAF_NOEXCEPT { return q * (*this); }

    /** Direct scalar multiplication.
     * @throw CryptoException if the input didn't decode.
     */
    inline SecureBuffer direct_scalarmul (
        const FixedBlock<SER_BYTES> &in,
        decaf_bool_t allow_identity=DECAF_FALSE,
        decaf_bool_t short_circuit=DECAF_TRUE
    ) const /*throw(CryptoException)*/;
        
    /** Direct scalar multiplication. */
    inline decaf_error_t DECAF_WARN_UNUSED direct_scalarmul_noexcept(
        FixedBuffer<SER_BYTES> &out,
        const FixedBlock<SER_BYTES> &in,
        decaf_bool_t allow_identity=DECAF_FALSE,
        decaf_bool_t short_circuit=DECAF_TRUE
    ) const DECAF_NOEXCEPT;
};

/** Element of prime-order elliptic curve group. */
class Point : public Serializable<Point> {
public:
    /** Wrapped C type */
    typedef $(c_ns)_point_t Wrapped;
    
    /** Size of a serialized element */
    static const size_t SER_BYTES = $(C_NS)_SER_BYTES;

    /** Bytes required for hash */
    static const size_t HASH_BYTES = $(C_NS)_HASH_BYTES;

    /** Bytes required for EdDSA encoding */
    static const size_t EDDSA_BYTES = DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES;

    /** Bytes required for EdDSA encoding */
    static const size_t LADDER_BYTES = DECAF_X$(gf_shortname)_PUBLIC_BYTES;
    
    /** Ratio due to EdDSA encoding */
    static const int EDDSA_ENCODE_RATIO = $(C_NS)_EDDSA_ENCODE_RATIO;
    
    /** Ratio due to EdDSA decoding */
    static const int EDDSA_DECODE_RATIO = $(C_NS)_EDDSA_DECODE_RATIO;
    
    /** Ratio due to ladder decoding */
    static const int LADDER_ENCODE_RATIO = DECAF_X$(gf_shortname)_ENCODE_RATIO;

    /** Size of a steganographically-encoded curve element.  If the point is random, the encoding
     * should look statistically close to a uniformly-random sequnece of STEG_BYTES bytes.
     */
    static const size_t STEG_BYTES = HASH_BYTES * 2;
    
    /** Number of bits in invert_elligator which are actually used. */
    static const unsigned int INVERT_ELLIGATOR_WHICH_BITS = $(C_NS)_INVERT_ELLIGATOR_WHICH_BITS;

    /** The c-level object. */
    Wrapped p;

    /** @cond internal */
    /** Don't initialize. */
    inline Point(const NOINIT &) DECAF_NOEXCEPT {}
    /** @endcond */

    /** Constructor sets to identity by default. */
    inline Point(const Wrapped &q = $(c_ns)_point_identity) DECAF_NOEXCEPT { $(c_ns)_point_copy(p,q); }

    /** Copy constructor. */
    inline Point(const Point &q) DECAF_NOEXCEPT { *this = q; }

    /** Assignment. */
    inline Point& operator=(const Point &q) DECAF_NOEXCEPT { $(c_ns)_point_copy(p,q.p); return *this; }

    /** Destructor securely zeorizes the point. */
    inline ~Point() DECAF_NOEXCEPT { $(c_ns)_point_destroy(p); }

    /** Construct from RNG */
    inline explicit Point(Rng &rng, bool uniform = true) DECAF_NOEXCEPT {
        if (uniform) {
            FixedArrayBuffer<2*HASH_BYTES> b(rng);
            set_to_hash(b);
        } else {
            FixedArrayBuffer<HASH_BYTES> b(rng);
            set_to_hash(b);
        }
    }

   /**
    * Initialize from a fixed-length byte string.
    * The all-zero string maps to the identity.
    *
    * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point,
    * or was the identity and allow_identity was DECAF_FALSE.
    */
    inline explicit Point(const FixedBlock<SER_BYTES> &buffer, bool allow_identity=true)
        /*throw(CryptoException)*/ {
        if (DECAF_SUCCESS != decode(buffer,allow_identity)) {
            throw CryptoException();
        }
    }

    /**
     * Initialize from C++ fixed-length byte string.
     * The all-zero string maps to the identity.
     *
     * @retval DECAF_SUCCESS the string was successfully decoded.
     * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point,
     * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined.
     */
    inline decaf_error_t DECAF_WARN_UNUSED decode (
        const FixedBlock<SER_BYTES> &buffer, bool allow_identity=true
    ) DECAF_NOEXCEPT {
        return $(c_ns)_point_decode(p,buffer.data(),allow_identity ? DECAF_TRUE : DECAF_FALSE);
    }

    /**
     * Initialize from C++ fixed-length byte string, like EdDSA.
     * The all-zero string maps to the identity.
     *
     * @retval DECAF_SUCCESS the string was successfully decoded.
     * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point.
     * Contents of the point are undefined.
     */
    inline decaf_error_t DECAF_WARN_UNUSED decode_like_eddsa_and_mul_by_ratio_noexcept (
        const FixedBlock<DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES> &buffer
    ) DECAF_NOEXCEPT {
        return $(c_ns)_point_decode_like_eddsa_and_mul_by_ratio(p,buffer.data());
    }
    
    /**
     * Decode from EDDSA, multiply by EDDSA_DECODE_RATIO, and ignore any
     * remaining cofactor information.
     * @throw CryptoException if the input point was invalid.
     */
    inline void decode_like_eddsa_and_mul_by_ratio(
        const FixedBlock<DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES> &buffer
    ) /*throw(CryptoException)*/ {
        if (DECAF_SUCCESS != decode_like_eddsa_and_mul_by_ratio_noexcept(buffer)) throw(CryptoException());
    }

    /** Multiply by EDDSA_ENCODE_RATIO and encode like EdDSA. */
    inline SecureBuffer mul_by_ratio_and_encode_like_eddsa() const {
        SecureBuffer ret(DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES);
        $(c_ns)_point_mul_by_ratio_and_encode_like_eddsa(ret.data(),p);
        return ret;
    }

    /** Multiply by EDDSA_ENCODE_RATIO and encode like EdDSA. */
    inline void mul_by_ratio_and_encode_like_eddsa(
        FixedBuffer<DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES> &out
    ) const {
        $(c_ns)_point_mul_by_ratio_and_encode_like_eddsa(out.data(),p);
    }

    /** Multiply by LADDER_ENCODE_RATIO and encode like X25519/X448. */
    inline SecureBuffer mul_by_ratio_and_encode_like_ladder() const {
        SecureBuffer ret(LADDER_BYTES);
        $(c_ns)_point_mul_by_ratio_and_encode_like_x$(gf_shortname)(ret.data(),p);
        return ret;
    }

    /** Multiply by LADDER_ENCODE_RATIO and encode like X25519/X448. */
    inline void mul_by_ratio_and_encode_like_ladder(FixedBuffer<LADDER_BYTES> &out) const {
        $(c_ns)_point_mul_by_ratio_and_encode_like_x$(gf_shortname)(out.data(),p);
    }

    /**
     * Map uniformly to the curve from a hash buffer.
     * The empty or all-zero string maps to the identity, as does the string "\\x01".
     * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform,
     * but the buffer will be zero-padded on the right.
     */
    static inline Point from_hash ( const Block &s ) DECAF_NOEXCEPT {
        Point p((NOINIT())); p.set_to_hash(s); return p;
    }

    /**
     * Map to the curve from a hash buffer.
     * The empty or all-zero string maps to the identity, as does the string "\\x01".
     * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform,
     * but the buffer will be zero-padded on the right.
     */
    inline void set_to_hash( const Block &s ) DECAF_NOEXCEPT {
        if (s.size() < HASH_BYTES) {
            SecureBuffer b(HASH_BYTES);
            memcpy(b.data(), s.data(), s.size());
            $(c_ns)_point_from_hash_nonuniform(p,b.data());
        } else if (s.size() == HASH_BYTES) {
            $(c_ns)_point_from_hash_nonuniform(p,s.data());
        } else if (s.size() < 2*HASH_BYTES) {
            SecureBuffer b(2*HASH_BYTES);
            memcpy(b.data(), s.data(), s.size());
            $(c_ns)_point_from_hash_uniform(p,b.data());
        } else {
            $(c_ns)_point_from_hash_uniform(p,s.data());
        }
    }

    /** Encode to string. The identity encodes to the all-zero string. */
    inline operator SecureBuffer() const {
        SecureBuffer buffer(SER_BYTES);
        $(c_ns)_point_encode(buffer.data(), p);
        return buffer;
    }

    /** Serializable instance */
    inline size_t ser_size() const DECAF_NOEXCEPT { return SER_BYTES; }

    /** Serializable instance */
    inline void serialize_into(unsigned char *buffer) const DECAF_NOEXCEPT {
        $(c_ns)_point_encode(buffer, p);
    }

    /** Point add. */
    inline Point operator+ (const Point &q) const DECAF_NOEXCEPT { Point r((NOINIT())); $(c_ns)_point_add(r.p,p,q.p); return r; }

    /** Point add. */
    inline Point &operator+=(const Point &q) DECAF_NOEXCEPT { $(c_ns)_point_add(p,p,q.p); return *this; }

    /** Point subtract. */
    inline Point operator- (const Point &q) const DECAF_NOEXCEPT { Point r((NOINIT())); $(c_ns)_point_sub(r.p,p,q.p); return r; }

    /** Point subtract. */
    inline Point &operator-=(const Point &q) DECAF_NOEXCEPT { $(c_ns)_point_sub(p,p,q.p); return *this; }

    /** Point negate. */
    inline Point operator- () const DECAF_NOEXCEPT { Point r((NOINIT())); $(c_ns)_point_negate(r.p,p); return r; }

    /** Double the point out of place. */
    inline Point times_two () const DECAF_NOEXCEPT { Point r((NOINIT())); $(c_ns)_point_double(r.p,p); return r; }

    /** Double the point in place. */
    inline Point &double_in_place() DECAF_NOEXCEPT { $(c_ns)_point_double(p,p); return *this; }

    /** Constant-time compare. */
    inline bool operator!=(const Point &q) const DECAF_NOEXCEPT { return ! $(c_ns)_point_eq(p,q.p); }

    /** Constant-time compare. */
    inline bool operator==(const Point &q) const DECAF_NOEXCEPT { return !!$(c_ns)_point_eq(p,q.p); }

    /** Scalar multiply. */
    inline Point operator* (const Scalar &s) const DECAF_NOEXCEPT { Point r((NOINIT())); $(c_ns)_point_scalarmul(r.p,p,s.s); return r; }

    /** Scalar multiply in place. */
    inline Point &operator*=(const Scalar &s) DECAF_NOEXCEPT { $(c_ns)_point_scalarmul(p,p,s.s); return *this; }

    /** Multiply by s.inverse(). If s=0, maps to the identity. */
    inline Point operator/ (const Scalar &s) const /*throw(CryptoException)*/ { return (*this) * s.inverse(); }

    /** Multiply by s.inverse(). If s=0, maps to the identity. */
    inline Point &operator/=(const Scalar &s) /*throw(CryptoException)*/ { return (*this) *= s.inverse(); }

    /** Validate / sanity check */
    inline bool validate() const DECAF_NOEXCEPT { return $(c_ns)_point_valid(p); }

    /** Double-scalar multiply, equivalent to q*qs + r*rs but faster. */
    static inline Point double_scalarmul (
        const Point &q, const Scalar &qs, const Point &r, const Scalar &rs
    ) DECAF_NOEXCEPT {
        Point p((NOINIT())); $(c_ns)_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p;
    }

    /** Dual-scalar multiply, equivalent to this*r1, this*r2 but faster. */
    inline void dual_scalarmul (
        Point &q1, Point &q2, const Scalar &r1, const Scalar &r2
    ) const DECAF_NOEXCEPT {
        $(c_ns)_point_dual_scalarmul(q1.p,q2.p,p,r1.s,r2.s);
    }

    /**
     * Double-scalar multiply, equivalent to q*qs + r*rs but faster.
     * For those who like their scalars before the point.
     */
    static inline Point double_scalarmul (
        const Scalar &qs, const Point &q, const Scalar &rs, const Point &r
    ) DECAF_NOEXCEPT {
        return double_scalarmul(q,qs,r,rs);
    }

    /**
     * Double-scalar multiply: this point by the first scalar and base by the second scalar.
     * @warning This function takes variable time, and may leak the scalars (or points, but currently
     * it doesn't).
     */
    inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) DECAF_NOEXCEPT {
        Point r((NOINIT())); $(c_ns)_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r;
    }

    /** Return a point equal to *this, whose internal data is rotated by a torsion element. */
    inline Point debugging_torque() const DECAF_NOEXCEPT {
        Point q;
        $(c_ns)_point_debugging_torque(q.p,p);
        return q;
    }

    /** Return a point equal to *this, whose internal data has a modified representation. */
    inline Point debugging_pscale(const FixedBlock<SER_BYTES> factor) const DECAF_NOEXCEPT {
        Point q;
        $(c_ns)_point_debugging_pscale(q.p,p,factor.data());
        return q;
    }

    /** Return a point equal to *this, whose internal data has a randomized representation. */
    inline Point debugging_pscale(Rng &r) const DECAF_NOEXCEPT {
        FixedArrayBuffer<SER_BYTES> sb(r);
        return debugging_pscale(sb);
    }

    /**
     * Modify buffer so that Point::from_hash(Buffer) == *this, and return DECAF_SUCCESS;
     * or leave buf unmodified and return DECAF_FAILURE.
     */
    inline decaf_error_t invert_elligator (
        Buffer buf, uint32_t hint
    ) const DECAF_NOEXCEPT {
        unsigned char buf2[2*HASH_BYTES];
        memset(buf2,0,sizeof(buf2));
        memcpy(buf2,buf.data(),(buf.size() > 2*HASH_BYTES) ? 2*HASH_BYTES : buf.size());
        decaf_bool_t ret;
        if (buf.size() > HASH_BYTES) {
            ret = decaf_successful($(c_ns)_invert_elligator_uniform(buf2, p, hint));
        } else {
            ret = decaf_successful($(c_ns)_invert_elligator_nonuniform(buf2, p, hint));
        }
        if (buf.size() < HASH_BYTES) {
            ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size());
        }
        for (size_t i=0; i<buf.size() && i<HASH_BYTES; i++) {
            buf[i] = (buf[i] & ~ret) | (buf2[i] &ret);
        }
        decaf_bzero(buf2,sizeof(buf2));
        return decaf_succeed_if(ret);
    }

    /** Steganographically encode this */
    inline SecureBuffer steg_encode(Rng &rng) const /*throw(std::bad_alloc, LengthException)*/ {
        SecureBuffer out(STEG_BYTES);
        decaf_error_t done;
        do {
            rng.read(Buffer(out).slice(HASH_BYTES-4,STEG_BYTES-HASH_BYTES+4));
            uint32_t hint = 0;
            for (int i=0; i<4; i++) { hint |= uint32_t(out[HASH_BYTES-4+i])<<(8*i); }
            done = invert_elligator(out, hint);
        } while (!decaf_successful(done));
        return out;
    }

    /** Return the base point of the curve. */
    static inline const Point base() DECAF_NOEXCEPT { return Point($(c_ns)_point_base); }

    /** Return the identity point of the curve. */
    static inline const Point identity() DECAF_NOEXCEPT { return Point($(c_ns)_point_identity); }
};

/**
 * Precomputed table of points.
 * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is.
 * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to
 * stack-allocate a 15kiB object anyway.
 */

/** @cond internal */
typedef $(c_ns)_precomputed_s Precomputed_U;
/** @endcond */
class Precomputed
    /** @cond internal */
    : protected OwnedOrUnowned<Precomputed,Precomputed_U>
    /** @endcond */
{
public:

    /** Destructor securely zeorizes the memory. */
    inline ~Precomputed() DECAF_NOEXCEPT { clear(); }

    /**
     * Initialize from underlying type, declared as a reference to prevent
     * it from being called with 0, thereby breaking override.
     *
     * The underlying object must remain valid throughout the lifetime of this one.
     *
     * By default, initializes to the table for the base point.
     *
     * @warning The empty initializer makes this equal to base, unlike the empty
     * initializer for points which makes this equal to the identity.
     */
    inline Precomputed (
        const Precomputed_U &yours = *$(c_ns)_precomputed_base
    ) DECAF_NOEXCEPT : OwnedOrUnowned<Precomputed,Precomputed_U>(yours) {}


#if __cplusplus >= 201103L
    /** Move-assign operator */
    inline Precomputed &operator=(Precomputed &&it) DECAF_NOEXCEPT {
        OwnedOrUnowned<Precomputed,Precomputed_U>::operator= (it);
        return *this;
    }

    /** Move constructor */
    inline Precomputed(Precomputed &&it) DECAF_NOEXCEPT : OwnedOrUnowned<Precomputed,Precomputed_U>() {
        *this = it;
    }

    /** Undelete copy operator */
    inline Precomputed &operator=(const Precomputed &it) DECAF_NOEXCEPT {
        OwnedOrUnowned<Precomputed,Precomputed_U>::operator= (it);
        return *this;
    }
#endif

    /**
     * Initilaize from point. Must allocate memory, and may throw.
     */
    inline Precomputed &operator=(const Point &it) /*throw(std::bad_alloc)*/ {
        alloc();
        $(c_ns)_precompute(ours.mine,it.p);
        return *this;
    }

    /**
     * Copy constructor.
     */
    inline Precomputed(const Precomputed &it) /*throw(std::bad_alloc)*/
        : OwnedOrUnowned<Precomputed,Precomputed_U>() { *this = it; }

    /**
     * Constructor which initializes from point.
     */
    inline explicit Precomputed(const Point &it) /*throw(std::bad_alloc)*/
        : OwnedOrUnowned<Precomputed,Precomputed_U>() { *this = it; }

    /** Fixed base scalarmul. */
    inline Point operator* (const Scalar &s) const DECAF_NOEXCEPT { Point r; $(c_ns)_precomputed_scalarmul(r.p,get(),s.s); return r; }

    /** Multiply by s.inverse(). If s=0, maps to the identity. */
    inline Point operator/ (const Scalar &s) const /*throw(CryptoException)*/ { return (*this) * s.inverse(); }

    /** Return the table for the base point. */
    static inline const Precomputed base() DECAF_NOEXCEPT { return Precomputed(); }

public:
    /** @cond internal */
    friend class OwnedOrUnowned<Precomputed,Precomputed_U>;
    static inline size_t size() DECAF_NOEXCEPT { return $(c_ns)_sizeof_precomputed_s; }
    static inline size_t alignment() DECAF_NOEXCEPT { return $(c_ns)_alignof_precomputed_s; }
    static inline const Precomputed_U * default_value() DECAF_NOEXCEPT { return $(c_ns)_precomputed_base; }
    /** @endcond */
};

/** X-only Diffie-Hellman ladder functions */
struct DhLadder {
public:
    /** Bytes in an X$(gf_shortname) public key. */
    static const size_t PUBLIC_BYTES = DECAF_X$(gf_shortname)_PUBLIC_BYTES;

    /** Bytes in an X$(gf_shortname) private key. */
    static const size_t PRIVATE_BYTES = DECAF_X$(gf_shortname)_PRIVATE_BYTES;

    /** Base point for a scalar multiplication. */
    static const FixedBlock<PUBLIC_BYTES> base_point() DECAF_NOEXCEPT {
        return FixedBlock<PUBLIC_BYTES>(decaf_x$(gf_shortname)_base_point);
    }

    /** Calculate and return a shared secret with public key.  */
    static inline SecureBuffer shared_secret(
        const FixedBlock<PUBLIC_BYTES> &pk,
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) /*throw(std::bad_alloc,CryptoException)*/ {
        SecureBuffer out(PUBLIC_BYTES);
        if (DECAF_SUCCESS != decaf_x$(gf_shortname)(out.data(), pk.data(), scalar.data())) {
            throw CryptoException();
        }
        return out;
    }

    /** Calculate and write into out a shared secret with public key, noexcept version.  */
    static inline decaf_error_t DECAF_WARN_UNUSED
    shared_secret_noexcept (
        FixedBuffer<PUBLIC_BYTES> &out,
        const FixedBlock<PUBLIC_BYTES> &pk,
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) DECAF_NOEXCEPT {
       return decaf_x$(gf_shortname)(out.data(), pk.data(), scalar.data());
    }

    /** Calculate and return a public key; equivalent to shared_secret(base_point(),scalar)
     * but possibly faster.
     * @deprecated Renamed to derive_public_key.
     */
    static inline SecureBuffer DECAF_DEPRECATED("Renamed to derive_public_key")
    generate_key(
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) /*throw(std::bad_alloc)*/ {
        SecureBuffer out(PUBLIC_BYTES);
        decaf_x$(gf_shortname)_derive_public_key(out.data(), scalar.data());
        return out;
    }

    /** Calculate and return a public key; equivalent to shared_secret(base_point(),scalar)
     * but possibly faster.
     */
    static inline SecureBuffer derive_public_key(
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) /*throw(std::bad_alloc)*/ {
        SecureBuffer out(PUBLIC_BYTES);
        decaf_x$(gf_shortname)_derive_public_key(out.data(), scalar.data());
        return out;
    }

    /** Calculate and return a public key into a fixed buffer;
     * equivalent to shared_secret(base_point(),scalar) but possibly faster.
     */
    static inline void
    derive_public_key_noexcept (
        FixedBuffer<PUBLIC_BYTES> &out,
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) DECAF_NOEXCEPT {
        decaf_x$(gf_shortname)_derive_public_key(out.data(), scalar.data());
    }

    /** Calculate and return a public key into a fixed buffer;
     * equivalent to shared_secret(base_point(),scalar) but possibly faster.
     * @deprecated Renamed to derive_public_key_noexcept.
     */
    static inline void
    DECAF_DEPRECATED("Renamed to derive_public_key_noexcept")
    generate_key_noexcept (
        FixedBuffer<PUBLIC_BYTES> &out,
        const FixedBlock<PRIVATE_BYTES> &scalar
    ) DECAF_NOEXCEPT {
        decaf_x$(gf_shortname)_derive_public_key(out.data(), scalar.data());
    }
};

}; /* struct $(cxx_ns) */

/** @cond internal */
inline SecureBuffer $(cxx_ns)::Scalar::direct_scalarmul (
    const FixedBlock<$(cxx_ns)::Point::SER_BYTES> &in,
    decaf_bool_t allow_identity,
    decaf_bool_t short_circuit
) const /*throw(CryptoException)*/ {
    SecureBuffer out($(cxx_ns)::Point::SER_BYTES);
    if (DECAF_SUCCESS !=
        $(c_ns)_direct_scalarmul(out.data(), in.data(), s, allow_identity, short_circuit)
    ) {
        throw CryptoException();
    }
    return out;
}

inline decaf_error_t $(cxx_ns)::Scalar::direct_scalarmul_noexcept (
    FixedBuffer<$(cxx_ns)::Point::SER_BYTES> &out,
    const FixedBlock<$(cxx_ns)::Point::SER_BYTES> &in,
    decaf_bool_t allow_identity,
    decaf_bool_t short_circuit
) const DECAF_NOEXCEPT {
    return $(c_ns)_direct_scalarmul(out.data(), in.data(), s, allow_identity, short_circuit);
}
/** @endcond */

$("/** Alternative name for %s, for backwards compatibility */\ntypedef %s %s;\n" % (cxx_ns,cxx_ns,altname) if altname else "")

#undef DECAF_NOEXCEPT
} /* namespace decaf */
