From b085fd9162fa3ff5b16788ced6d11a71ffe5a5fe Mon Sep 17 00:00:00 2001 From: mdw Date: Sun, 13 Jan 2002 13:48:44 +0000 Subject: [PATCH] Further progress. --- ec-exp.h | 76 +++++++++++++++++++++++++++++++++ ec-prime.c | 89 ++++++++++++++++++++++++++++++++++---- ec.c | 97 +++++++++++++++++++++--------------------- ec.h | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- f-prime.c | 13 +++++- field.h | 7 ++- 6 files changed, 341 insertions(+), 82 deletions(-) create mode 100644 ec-exp.h diff --git a/ec-exp.h b/ec-exp.h new file mode 100644 index 0000000..c760c43 --- /dev/null +++ b/ec-exp.h @@ -0,0 +1,76 @@ +/* -*-c-*- + * + * $Id: ec-exp.h,v 1.1 2002/01/13 13:48:44 mdw Exp $ + * + * Exponentiation operations for elliptic curves + * + * (c) 2001 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: ec-exp.h,v $ + * Revision 1.1 2002/01/13 13:48:44 mdw + * Further progress. + * + */ + +#ifndef CATACOMB_EC_EXP_H +#define CATACOMB_EC_EXP_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Exponentation definitions -----------------------------------------*/ + +#define EXP_TYPE ec + +#define EXP_COPY(d, x) do { \ + d.x = MP_COPY(x.x); \ + d.y = MP_COPY(x.y); \ + d.z = x.z ? MP_COPY(x.x) : MP_NEW; \ +} while (0) +#define EXP_DROP(x) EC_DESTROY(c, &x) + +#define EXP_MUL(a, x) EC_ADD(c, &a, &a, &x) +#define EXP_SQR(a) EC_DBL(c, &a, &a); + +#define EXP_SETMUL(d, x, y) do { \ + EC_CREATE(&d); \ + EC_ADD(c, &d, &x, &y); \ +} while (0) +#define EXP_SETSQR(d, x) do { \ + EC_CREATE(&d); \ + EC_DBL(c, &d, &x); \ +} while (0) + +#include "exp.h" + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ec-prime.c b/ec-prime.c index 5a806da..09733ce 100644 --- a/ec-prime.c +++ b/ec-prime.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: ec-prime.c,v 1.1 2001/04/29 18:12:33 mdw Exp $ + * $Id: ec-prime.c,v 1.2 2002/01/13 13:48:44 mdw Exp $ * * Elliptic curves over prime fields * @@ -30,6 +30,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: ec-prime.c,v $ + * Revision 1.2 2002/01/13 13:48:44 mdw + * Further progress. + * * Revision 1.1 2001/04/29 18:12:33 mdw * Prototype version. * @@ -48,25 +51,93 @@ typedef struct ecctx { /*----- Main code ---------------------------------------------------------*/ -static void ecadd(ec_curve *c, ec *d, const ec *a, const ec *b) +static ec *ecneg(ec_cuvrve *c, ec *d, const ec *p) +{ + EC_COPY(d, p); + d->y = F_NEG(c->f, d->y, d->y); + return (d); +} + +static ec *ecdbl(ec_curve *c, ec *d, const ec *a) { - /* --- Deal with the simple cases --- */ + if (EC_ATINF(a)) + EC_SETINF(d); + else if (!MP_LEN(a->y)) + EC_COPY(d, a); + else { + field *f = c->f; + ecctx *cc = (ecctx *)c; + mp *lambda; + mp *dy, *dx; + + dx = F_SQR(f, MP_NEW, a->x); + dy = F_DBL(f, MP_NEW, a->y); + dx = F_TPL(f, dx, dx); + dx = F_ADD(f, dx, dx, cc->a); + dy = F_INV(f, dy, dy); + lambda = F_MUL(d, MP_NEW, dx, dy); + + dx = F_SQR(f, dx, lambda); + dy = F_DBL(d, dy, a->x); + dx = F_SUB(f, dx, dx, dy); + dy = F_SUB(f, dy, a->x, dx); + dy = F_MUL(f, dy, lambda, dy); + dy = F_SUB(f, dy, dy, a->y); + EC_DESTROY(d); + d->x = dx; + d->y = dy; + d->z = 0; + MP_DROP(lambda); + } + return (d); +} + +static ec *ecadd(ec_curve *c, ec *d, const ec *a, const ec *b) +{ if (a == b) ecdbl(c, d, a); else if (EC_ATINF(a)) EC_COPY(d, b); else if (EC_ATINF(b)) EC_COPY(d, a); - else if (MP_EQ(a->x, b->x) && MP_EQ(a->z, b->z)) { - if ((a->y->f ^ b->y->f) & MP_NEG) + else { + field *f = c->f; + mp *lambda; + mp *dy, *dx; + + if (!MP_EQ(a->x, b->x)) { + dy = F_SUB(f, MP_NEW, a->y, b->y); + dx = F_SUB(f, MP_NEW, a->x, b->x); + dx = F_INV(f, dx, dx); + lambda = F_MUL(f, MP_NEW, dy, dx); + } else if (!MP_LEN(a->y) || !MP_EQ(a->y, b->y)) { EC_SETINF(d); - else - ecdbl(c, d, a); - } else { + return (d); + } else { + ecctx *cc = (ecctx *)c; + dx = F_SQR(f, MP_NEW, a->x); + dx = F_TPL(f, dx, dx); + dx = F_ADD(f, dx, dx, cc->a); + dy = F_DBL(f, MP_NEW, a->y); + dy = F_INV(f, dy, dy); + lambda = F_MUL(d, MP_NEW, dx, dy); + } + + dx = F_SQR(f, dx, lambda); + dx = F_SUB(f, dx, dx, a->x); + dx = F_SUB(f, dx, dx, b->x); + dy = F_SUB(f, dy, b->x, dx); + dy = F_MUL(f, dy, lambda, dy); + dy = F_SUB(f, dy, dy, b->y); - /* --- + EC_DESTROY(d); + d->x = dx; + d->y = dy; + d->z = 0; + MP_DROP(lambda); } + return (d); } /*----- That's all, folks -------------------------------------------------*/ diff --git a/ec.c b/ec.c index 37d8ad3..ce4e428 100644 --- a/ec.c +++ b/ec.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: ec.c,v 1.2 2001/05/07 17:29:44 mdw Exp $ + * $Id: ec.c,v 1.3 2002/01/13 13:48:44 mdw Exp $ * * Elliptic curve definitions * @@ -30,6 +30,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: ec.c,v $ + * Revision 1.3 2002/01/13 13:48:44 mdw + * Further progress. + * * Revision 1.2 2001/05/07 17:29:44 mdw * Treat projective coordinates as an internal representation. Various * minor interface changes. @@ -42,6 +45,7 @@ /*----- Header files ------------------------------------------------------*/ #include "ec.h" +#include "ec-exp.h" /*----- Trivial wrappers --------------------------------------------------*/ @@ -186,6 +190,28 @@ ec *ec_projout(ec_curve *c, ec *d, const ec *p) return (d); } +/* --- @ec_stdsub@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an elliptic curve + * @ec *d@ = pointer to the destination + * @const ec *a, *b@ = the operand points + * + * Returns: The destination @d@. + * + * Use: Standard point subtraction operation, in terms of negation + * and addition. This isn't as efficient as a ready-made + * subtraction operator. + */ + +ec *ec_stdsub(ec_curve *c, ec *d, const ec *a, const ec *b) +{ + ec t = EC_INIT; + EC_NEG(c, &t, b); + EC_SUB(c, d, a, &t); + EC_DESTROY(&t); + return (d); +} + /*----- Real arithmetic ---------------------------------------------------*/ /* --- @ec_find@ --- * @@ -249,65 +275,42 @@ ec *ec_dbl(ec_curve *c, ec *d, const ec *p) return (EC_OUT(c, d, d)); } -/* --- @ec_mul@ --- * +/* --- @ec_imul@, @ec_mul@ --- * * * Arguments: @ec_curve *c@ = pointer to an elliptic curve * @ec *d@ = pointer to the destination point * @const ec *p@ = pointer to the generator point * @mp *n@ = integer multiplier * - * Returns: --- + * Returns: The destination @d@. * - * Use: Multiplies a point by a scalar, returning %$n p$%. + * Use: Multiplies a point by a scalar, returning %$n p$%. The + * @imul@ variant uses internal representations for argument + * and result. */ -ec *ec_mul(ec_curve *c, ec *d, const ec *p, mp *n) +ec *ec_imul(ec_curve *c, ec *d, const ec *p, mp *n) { - mpscan sc; - ec g = EC_INIT; - unsigned sq = 0; + ec t = EC_INIT; + EC_COPY(&t, p); + if (t.x && (n->f & MP_BURN)) + t.x->f |= MP_BURN; + MP_SHRINK(n); EC_SETINF(d); - if (EC_ATINF(p)) - return; - - mp_rscan(&sc, n); - if (!MP_RSTEP(&sc)) - goto exit; - while (!MP_RBIT(&sc)) - MP_RSTEP(&sc); - - EC_IN(c, &g, p); - if ((n->f & MP_BURN) && !(g.x->f & MP_BURN)) - MP_DEST(g.x, 0, MP_BURN); - if ((n->f & MP_BURN) && !(g.y->f & MP_BURN)) - MP_DEST(g.y, 0, MP_BURN); - - for (;;) { - EC_ADD(c, d, d, &g); - sq = 0; - for (;;) { - if (!MP_RSTEP(&sc)) - goto done; - if (MP_RBIT(&sc)) - break; - sq++; - } - sq++; - while (sq) { - EC_DBL(c, d, d); - sq--; - } - } - -done: - while (sq) { - EC_DBL(c, d, d); - sq--; - } + if (MP_LEN(n) == 0) + ; + else if (MP_LEN(n) < EXP_THRESH) + EXP_SIMPLE(&d, t, n); + else + EXP_WINDOW(&d, t, n); + return (d); +} - EC_DESTROY(&g); -exit: +ec *ec_mul(ec_curve *c, ec *d, const ec *p, mp *n) +{ + EC_IN(c, d, p); + ec_imul(c, d, d, n); return (EC_OUT(c, d, d)); } diff --git a/ec.h b/ec.h index 12ff823..2efe939 100644 --- a/ec.h +++ b/ec.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: ec.h,v 1.2 2001/05/07 17:29:44 mdw Exp $ + * $Id: ec.h,v 1.3 2002/01/13 13:48:44 mdw Exp $ * * Elliptic curve definitions * @@ -30,6 +30,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: ec.h,v $ + * Revision 1.3 2002/01/13 13:48:44 mdw + * Further progress. + * * Revision 1.2 2001/05/07 17:29:44 mdw * Treat projective coordinates as an internal representation. Various * minor interface changes. @@ -53,32 +56,47 @@ /*----- Data structures ---------------------------------------------------*/ +/* --- An elliptic curve representation --- */ + typedef struct ec_curve { const struct ec_ops *ops; /* Curve operations */ field *f; /* Underlying field structure */ } ec_curve; +/* --- An elliptic curve point --- */ + typedef struct ec { mp *x, *y; /* Point coordinates */ mp *z; /* Common denominator (or null) */ } ec; +/* --- A factor for simultaneous multiplication --- */ + +typedef struct ec_mulfactor { + ec base; /* The point */ + ec *exp; /* The exponent */ +} ec_mulfactor; + +/* --- Elliptic curve operations --- */ + typedef struct ec_ops { void (*destroy)(ec_curve */*c*/); ec *(*in)(ec_curve */*c*/, ec */*d*/, const ec */*p*/); ec *(*out)(ec_curve */*c*/, ec */*d*/, const ec */*p*/); ec *(*find)(ec_curve */*c*/, ec */*d*/, mp */*x*/); + ec *(*neg)(ec_curve */*c*/, ec */*d*/, const ec */*p*/); ec *(*add)(ec_curve */*c*/, ec */*d*/, const ec */*p*/, const ec */*q*/); + ec *(*sub)(ec_curve */*c*/, ec */*d*/, const ec */*p*/, const ec */*q*/); ec *(*dbl)(ec_curve */*c*/, ec */*d*/, const ec */*p*/); } ec_ops; -#define EC_DESTROY(c) (c)->ops->destroy((c)) - #define EC_IN(c, d, p) (c)->ops->in((c), (d), (p)) #define EC_OUT(c, d, p) (c)->ops->in((c), (d), (p)) #define EC_FIND(c, d, x) (c)->ops->find((c), (d), (x)) +#define EC_NEG(c, d, x) (c)->ops->neg((c), (d), (x)) #define EC_ADD(c, d, p, q) (c)->ops->add((c), (d), (p), (q)) +#define EC_SUB(c, d, p, q) (c)->ops->sub((c), (d), (p), (q)) #define EC_DBL(c, d, p) (c)->ops->dbl((c), (d), (p)) /*----- Simple memory management things -----------------------------------*/ @@ -175,9 +193,9 @@ extern ec *ec_setinf(ec */*p*/); if (EC_ATINF(p)) \ _d->x = _d->y = _d->z = MP_NEW; \ else { \ - _d->x = _p->x; \ - _d->y = _p->y; \ - _d->z = _p->z; \ + _d->x = MP_COPY(_p->x); \ + _d->y = MP_COPY(_p->y); \ + _d->z = _p->z ? MP_COPY(_p->z) : MP_NEW; \ } \ } \ } while (0) @@ -186,33 +204,31 @@ extern ec *ec_copy(ec */*d*/, const ec */*p*/); /*----- Interesting arithmetic --------------------------------------------*/ -/* --- @ec_denorm@ --- * +/* --- @ec_in@ --- * * * Arguments: @ec_curve *c@ = pointer to an elliptic curve * @ec *d@ = pointer to the destination point * @const ec *p@ = pointer to the source point * - * Returns: --- + * Returns: The destination point. * - * Use: Denormalizes the given point, converting to internal - * representations and setting the denominator to 1. + * Use: Converts a point to internal representation. */ -extern void ec_denorm(ec_curve */*c*/, ec */*d*/, const ec */*p*/); +extern ec *ec_in(ec_curve */*c*/, ec */*d*/, const ec */*p*/); -/* --- @ec_norm@ --- * +/* --- @ec_out@ --- * * * Arguments: @ec_curve *c@ = pointer to an elliptic curve * @ec *d@ = pointer to the destination point * @const ec *p@ = pointer to the source point * - * Returns: --- + * Returns: The destination point. * - * Use: Normalizes the given point, by dividing through by the - * denominator and returning to external representation. + * Use: Converts a point to external representation. */ -extern void ec_norm(ec_curve */*c*/, ec */*d*/, const ec */*p*/); +extern ec *ec_out(ec_curve */*c*/, ec */*d*/, const ec */*p*/); /* --- @ec_find@ --- * * @@ -220,12 +236,28 @@ extern void ec_norm(ec_curve */*c*/, ec */*d*/, const ec */*p*/); * @ec *d@ = pointer to the destination point * @mp *x@ = a possible x-coordinate * - * Returns: Zero if OK, nonzero if there isn't a point there. + * Returns: The destination if OK, or null if no point was found. * - * Use: Finds a point on an elliptic curve with a given x-coordinate. + * Use: Finds a point on an elliptic curve with a given + * x-coordinate. If there is no point with the given + * %$x$%-coordinate, a null pointer is returned and the + * destination is left invalid. */ -extern void ec_find(ec_curve */*c*/, ec */*d*/, mp */*x*/); +extern ec *ec_find(ec_curve */*c*/, ec */*d*/, mp */*x*/); + +/* --- @ec_neg@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an elliptic curve + * @ec *d@ = pointer to the destination point + * @const ec *p@ = pointer to the operand point + * + * Returns: The destination point. + * + * Use: Computes the negation of the given point. + */ + +extern ec *ec_neg(ec_curve */*c*/, ec */*d*/, const ec */*p*/); /* --- @ec_add@ --- * * @@ -241,6 +273,20 @@ extern void ec_find(ec_curve */*c*/, ec */*d*/, mp */*x*/); extern ec *ec_add(ec_curve */*c*/, ec */*d*/, const ec */*p*/, const ec */*q*/); +/* --- @ec_sub@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an elliptic curve + * @ec *d@ = pointer to the destination point + * @const ec *p, *q@ = pointers to the operand points + * + * Returns: The destination @d@. + * + * Use: Subtracts one point from another on an elliptic curve. + */ + +extern ec *ec_sub(ec_curve */*c*/, ec */*d*/, + const ec */*p*/, const ec */*q*/); + /* --- @ec_dbl@ --- * * * Arguments: @ec_curve *c@ = pointer to an elliptic curve @@ -254,7 +300,7 @@ extern ec *ec_add(ec_curve */*c*/, ec */*d*/, extern ec *ec_dbl(ec_curve */*c*/, ec */*d*/, const ec */*p*/); -/* --- @ec_mul@ --- * +/* --- @ec_mul@, @ec_imul@ --- * * * Arguments: @ec_curve *c@ = pointer to an elliptic curve * @ec *d@ = pointer to the destination point @@ -263,10 +309,31 @@ extern ec *ec_dbl(ec_curve */*c*/, ec */*d*/, const ec */*p*/); * * Returns: The destination @d@. * - * Use: Multiplies a point by a scalar, returning %$n p$%. + * Use: Multiplies a point by a scalar, returning %$n p$%. The + * @imul@ variant uses internal representations for argument + * and result. */ extern ec *ec_mul(ec_curve */*c*/, ec */*d*/, const ec */*p*/, mp */*n*/); +extern ec *ec_imul(ec_curve */*c*/, ec */*d*/, const ec */*p*/, mp */*n*/); + +/* --- @ec_mmul@, @ec_immul@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an elliptic curve + * @ec *d@ = pointer to the destination point + * @const ec_mulfactor *f@ = pointer to vector of factors + * @size_t n@ = number of factors + * + * Returns: The destination @d@. + * + * Use: Does simultaneous point multiplication. The @immul@ variant + * uses internal representations for arguments and result. + */ + +extern ec *ec_mmul(ec_curve */*c*/, ec */*d*/, + const ec_mulfactor */*f*/, size_t /*n*/); +extern ec *ec_immul(ec_curve */*c*/, ec */*d*/, + const ec_mulfactor */*f*/, size_t /*n*/); /*----- Standard curve operations -----------------------------------------*/ @@ -301,9 +368,35 @@ extern ec *ec_idout(ec_curve */*c*/, ec */*d*/, const ec */*p*/); extern ec *ec_projin(ec_curve */*c*/, ec */*d*/, const ec */*p*/); extern ec *ec_projout(ec_curve */*c*/, ec */*d*/, const ec */*p*/); +/* --- @ec_stdsub@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an elliptic curve + * @ec *d@ = pointer to the destination + * @const ec *a, *b@ = the operand points + * + * Returns: The destination @d@. + * + * Use: Standard point subtraction operation, in terms of negation + * and addition. This isn't as efficient as a ready-made + * subtraction operator. + */ + +extern ec *ec_stdsub(ec_curve */*c*/, ec */*d*/, const ec */*p*/); + /*----- Creating curves ---------------------------------------------------*/ -/* --- @ec_prime@ --- * +/* --- @ec_destroycurve@ --- * + * + * Arguments: @ec_curve *c@ = pointer to an ellptic curve + * + * Returns: --- + * + * Use: Destroys a description of an elliptic curve. + */ + +extern void ec_destroycurve(ec_curve */*c*/); + +/* --- @ec_prime@, @ec_primeproj@ --- * * * Arguments: @field *f@ = the underyling field for this elliptic curve * @mp *a, *b@ = the coefficients for this curve @@ -311,10 +404,12 @@ extern ec *ec_projout(ec_curve */*c*/, ec */*d*/, const ec */*p*/); * Returns: A pointer to the curve. * * Use: Creates a curve structure for an elliptic curve defined over - * a prime field. + * a prime field. The @primeproj@ variant uses projective + * coordinates, which can be a win. */ extern ec_curve *ec_prime(field */*f*/, mp */*a*/, mp */*b*/); +extern ec_curve *ec_primeproj(field */*f*/, mp */*a*/, mp */*b*/); /* --- @ec_bin@ --- * * diff --git a/f-prime.c b/f-prime.c index 1a7b9e8..a9673b8 100644 --- a/f-prime.c +++ b/f-prime.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: f-prime.c,v 1.1 2001/04/29 18:12:33 mdw Exp $ + * $Id: f-prime.c,v 1.2 2002/01/13 13:48:44 mdw Exp $ * * Prime fields with Montgomery arithmetic * @@ -30,6 +30,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: f-prime.c,v $ + * Revision 1.2 2002/01/13 13:48:44 mdw + * Further progress. + * * Revision 1.1 2001/04/29 18:12:33 mdw * Prototype version. * @@ -72,6 +75,12 @@ static mp *fout(field *ff, mp *d, mp *x) return (mpmont_reduce(&f->mm, d, x)); } +static mp *fneg(field *ff, mp *d, mp *x) +{ + fctx *f = (fctx *)ff; + return (mp_sub(d, f->mm.m, x)); +} + static mp *fadd(field *ff, mp *d, mp *x, mp *y) { return (mp_add(d, x, y)); @@ -137,7 +146,7 @@ static mp *fsqrt(field *ff, mp *d, mp *x) static field_ops fops = { fdestroy, fin, fout, - fadd, fsub, fmul, fsqr, finv, freduce, + fneg, fadd, fsub, fmul, fsqr, finv, freduce, fdbl, ftpl, fsqrt }; diff --git a/field.h b/field.h index 87d3865..5a70649 100644 --- a/field.h +++ b/field.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: field.h,v 1.2 2001/05/07 17:30:13 mdw Exp $ + * $Id: field.h,v 1.3 2002/01/13 13:48:44 mdw Exp $ * * Definitions for field arithmetic * @@ -30,6 +30,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: field.h,v $ + * Revision 1.3 2002/01/13 13:48:44 mdw + * Further progress. + * * Revision 1.2 2001/05/07 17:30:13 mdw * Add an internal-representation no-op function. * @@ -67,6 +70,7 @@ typedef struct field_ops { mp *(*in)(field */*f*/, mp */*d*/, mp */*x*/); mp *(*out)(field */*f*/, mp */*d*/, mp */*x*/); + mp *(*neg)(field */*f*/, mp */*d*/, mp */*x*/); mp *(*add)(field */*f*/, mp */*d*/, mp */*x*/, mp */*y*/); mp *(*sub)(field */*f*/, mp */*d*/, mp */*x*/, mp */*y*/); mp *(*mul)(field */*f*/, mp */*d*/, mp */*x*/, mp */*y*/); @@ -86,6 +90,7 @@ typedef struct field_ops { #define F_IN(f, d, x) (f)->ops->in((f), (d), (x)) #define F_OUT(f, d, x) (f)->ops->out((f), (d), (x)) +#define F_NEG(f, d, x) (f)->ops->neg((f), (d), (x)) #define F_ADD(f, d, x, y) (f)->ops->add((f), (d), (x), (y)) #define F_SUB(f, d, x, y) (f)->ops->sub((f), (d), (x), (y)) #define F_MUL(f, d, x, y) (f)->ops->mul((f), (d), (x), (y)) -- 2.11.0