``````package Math::Prime::Util::ECAffinePoint;
use strict;
use warnings;
use Carp qw/carp croak confess/;

BEGIN {
\$Math::Prime::Util::ECAffinePoint::AUTHORITY = 'cpan:DANAJ';
\$Math::Prime::Util::ECAffinePoint::VERSION = '0.73';
}

BEGIN {
do { require Math::BigInt;  Math::BigInt->import(try=>"GMP,Pari"); }
unless defined \$Math::BigInt::VERSION;
}

# Pure perl (with Math::BigInt) manipulation of Elliptic Curves
# in affine coordinates.  Should be split into a point class.

sub new {
my (\$class, \$a, \$b, \$n, \$x, \$y) = @_;
\$a = Math::BigInt->new("\$a") unless ref(\$a) eq 'Math::BigInt';
\$b = Math::BigInt->new("\$b") unless ref(\$b) eq 'Math::BigInt';
\$n = Math::BigInt->new("\$n") unless ref(\$n) eq 'Math::BigInt';
\$x = Math::BigInt->new("\$x") unless ref(\$x) eq 'Math::BigInt';
\$y = Math::BigInt->new("\$y") unless ref(\$y) eq 'Math::BigInt';

croak "n must be >= 2" unless \$n >= 2;
\$a->bmod(\$n);
\$b->bmod(\$n);

my \$self = {
a => \$a,
b => \$b,
n => \$n,
x => \$x,
y => \$y,
f => \$n->copy->bone,
};

bless \$self, \$class;
return \$self;
}

sub _add {
my (\$self, \$P1x, \$P1y, \$P2x, \$P2y) = @_;
my \$n = \$self->{'n'};

if (\$P1x == \$P2x) {
my \$t = (\$P1y + \$P2y) % \$n;
return (Math::BigInt->bzero,Math::BigInt->bone) if \$t == 0;
}
my \$deltax = (\$P2x - \$P1x) % \$n;
\$deltax->bmodinv(\$n);
return (Math::BigInt->bzero,Math::BigInt->bone) if \$deltax eq "NaN";

my \$deltay = (\$P2y - \$P1y) % \$n;
my \$m = (\$deltay * \$deltax) % \$n;   # m = deltay / deltax

my \$x = (\$m*\$m - \$P1x - \$P2x) % \$n;
my \$y = (\$m*(\$P1x - \$x) - \$P1y) % \$n;
return (\$x,\$y);
}

sub _double {
my (\$self, \$P1x, \$P1y) = @_;
my \$n = \$self->{'n'};

my \$m = 2*\$P1y;
\$m->bmodinv(\$n);
return (Math::BigInt->bzero,Math::BigInt->bone) if \$m eq "NaN";

\$m = ((3*\$P1x*\$P1x + \$self->{'a'}) * \$m) % \$n;

my \$x = (\$m*\$m - 2*\$P1x) % \$n;
my \$y = (\$m*(\$P1x - \$x) - \$P1y) % \$n;
return (\$x,\$y);
}

sub _inplace_add {
my (\$P1x, \$P1y, \$x, \$y, \$n) = @_;

if (\$P1x == \$x) {
my \$t = (\$P1y + \$y) % \$n;
if (\$t == 0) {
(\$_[2],\$_[3]) = (Math::BigInt->bzero,Math::BigInt->bone);
return;
}
}
my \$deltax = (\$x - \$P1x) % \$n;
\$deltax->bmodinv(\$n);
if (\$deltax eq 'NaN') {
(\$_[2],\$_[3]) = (Math::BigInt->bzero,Math::BigInt->bone);
return;
}
my \$deltay = (\$y - \$P1y) % \$n;
my \$m = (\$deltay * \$deltax) % \$n;   # m = deltay / deltax

\$_[2] = (\$m*\$m - \$P1x - \$x) % \$n;
\$_[3] = (\$m*(\$P1x - \$_[2]) - \$P1y) % \$n;
}
sub _inplace_double {
my(\$x, \$y, \$a, \$n) = @_;
my \$m = \$y+\$y;
\$m->bmodinv(\$n);
if (\$m eq 'NaN') {
(\$_[0],\$_[1]) = (Math::BigInt->bzero,Math::BigInt->bone);
return;
}
\$m->bmul(\$x->copy->bmul(\$x)->bmul(3)->badd(\$a))->bmod(\$n);

my \$xin = \$x->copy;
\$_[0] = (\$m*\$m - \$x - \$x) % \$n;
\$_[1] = (\$m*(\$xin - \$_[0]) - \$y) % \$n;
}

sub mul {
my (\$self, \$k) = @_;
my \$x = \$self->{'x'};
my \$y = \$self->{'y'};
my \$a = \$self->{'a'};
my \$n = \$self->{'n'};
my \$f = \$self->{'f'};
if (ref(\$k) eq 'Math::BigInt' && \$k < ''.~0) {
if (\$] >= 5.008 || ~0 == 4294967295) {
\$k = int(\$k->bstr);
} elsif (\$] < 5.008 && ~0 > 4294967295 && \$k < 562949953421312) {
\$k = unpack('Q',pack('Q',\$k->bstr));
}
}

my \$Bx = \$n->copy->bzero;
my \$By = \$n->copy->bone;
my \$v = 1;

while (\$k > 0) {
if ( (\$k % 2) != 0) {
\$k--;
\$f->bmul(\$Bx-\$x)->bmod(\$n);
if    (\$x->is_zero && \$y->is_one)   { }
elsif (\$Bx->is_zero && \$By->is_one) { (\$Bx,\$By) = (\$x,\$y); }
else                                { _inplace_add(\$x,\$y,\$Bx,\$By,\$n); }
} else {
\$k >>= 1;
\$f->bmul(2*\$y)->bmod(\$n);
_inplace_double(\$x,\$y,\$a,\$n);
}
}
\$f = Math::BigInt::bgcd(\$f, \$n);
\$self->{'x'} = \$Bx;
\$self->{'y'} = \$By;
\$self->{'f'} = \$f;
return \$self;
}

sub add {
my (\$self, \$other) = @_;
croak "add takes a EC point"
unless ref(\$other) eq 'Math::Prime::Util::ECAffinePoint';
croak "second point is not on the same curve"
unless \$self->{'a'} == \$other->{'a'} &&
\$self->{'b'} == \$other->{'b'} &&
\$self->{'n'} == \$other->{'n'};

(\$self->{'x'}, \$self->{'y'}) = \$self->_add(\$self->{'x'}, \$self->{'y'},
\$other->{'x'}, \$other->{'y'});
return \$self;
}

sub a { return shift->{'a'}; }
sub b { return shift->{'b'}; }
sub n { return shift->{'n'}; }
sub x { return shift->{'x'}; }
sub y { return shift->{'y'}; }
sub f { return shift->{'f'}; }

sub is_infinity {
my \$self = shift;
return (\$self->{'x'}->is_zero() && \$self->{'y'}->is_one());
}

1;

__END__

# ABSTRACT: Elliptic curve operations for affine points

=pod

=encoding utf8

=for stopwords mul

=for test_synopsis use v5.14;  my(\$a,\$b,\$n,\$k,\$ECP2);

=head1 NAME

Math::Prime::Util::ECAffinePoint - Elliptic curve operations for affine points

=head1 VERSION

Version 0.73

=head1 SYNOPSIS

# Create a point on a curve (a,b,n) with coordinates 0,1
my \$ECP = Math::Prime::Util::ECAffinePoint->new(\$a, \$b, \$n, 0, 1);

# scalar multiplication by k.
\$ECP->mul(\$k);

# add two points on the same curve
\$ECP->add(\$ECP2);

say "P = O" if \$ECP->is_infinity();

=head1 DESCRIPTION

This really should just be in Math::EllipticCurve.

To write.

=head1 FUNCTIONS

=head2 new

\$point = Math::Prime::Util::ECAffinePoint->new(a, b, n, x, y);

Returns a new point at C<(x,y,1)> on the curve C<(a,b,n)>.

=head2 a

=head2 b

=head2 n

Returns the C<a>, C<b>, or C<n> values that describe the curve.

=head2 x

=head2 y

Returns the C<x> or C<y> values that define the point on the curve.

=head2 f

Returns a possible factor found during EC multiplication.

=head2 add

Takes another point on the same curve as an argument and adds it this point.

=head2 mul

Takes an integer and performs scalar multiplication of the point.

=head2 is_infinity

Returns true if the point is (0,1), which is the point at infinity for
the affine coordinates.

=head1 SEE ALSO

L<Math::EllipticCurve::Prime>

This really should just be in a L<Math::EllipticCurve> module.

=head1 AUTHORS

Dana Jacobsen E<lt>dana@acm.orgE<gt>

=head1 COPYRIGHT

Copyright 2012-2013 by Dana Jacobsen E<lt>dana@acm.orgE<gt>

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=cut
``````