# -*-perl-*-
#
# $Id$
#
# Binary polynomial arithmetic
#
# (c) 2004 Straylight/Edgeware
#

#----- Licensing notice -----------------------------------------------------
#
# This file is part of the Perl interface to Catacomb.
#
# Catacomb/Perl is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# Catacomb/Perl 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Catacomb/Perl; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#----- Binary polynomials ---------------------------------------------------

package Catacomb::GF;
use Catacomb::Base;
use Catacomb::MP;
use Carp;

@ISA = qw(Catacomb::MP);

sub gf { new(Catacomb::GF, $_[0]); }
sub gf_loadb { loadb(Catacomb::GF, $_[0]); }
sub gf_loadl { loadl(Catacomb::GF, $_[0]); }
sub gf_fromstring { fromstring(Catacomb::GF, $_[0]); }

use overload
  '+' => sub { _binop(\&add, @_); },
  '-' => sub { _binop(\&add, @_); },
  '*' => sub { _binop(\&mul, @_); },
  '/' => sub { _binop(\&div, @_); },
  '%' => sub { _binop(\&mod, @_); },
  '&' => sub { _binop(\&Catacomb::MP::and, @_); },
  '|' => sub { _binop(\&Catacomb::MP::or, @_); },
  '^' => sub { _binop(\&Catacomb::MP::xor, @_); },
  '**' => sub { _binop(\&pow, @_); },
  '>>' => sub { new(undef, &Catacomb::MP::lsr(@_[0, 1])); },
  '<<' => sub { new(undef, &Catacomb::MP::lsl(@_[0, 1])); },
  '~' => sub { new(undef, &Catacomb::MP::not($_[0])) },
  '==' => sub { _binop(\&Catacomb::MP::eq, @_); },
  '!=' => sub { !_binop(\&Catacomb::MP::eq, @_); },
  'eq' => sub { _binop(\&Catacomb::MP::eq, @_); },
  'ne' => sub { !_binop(\&Catacomb::MP::eq, @_); },
  '""' => sub { "0x" . &Catacomb::MP::tostring($_[0], 16); },
  'neg' => sub { $_[0]; },
  '0+' => sub { &Catacomb::MP::toint($_[0]); };

sub binpolyfield {
  croak("Usage: Catacomb::GF::binpolyfield(p)") unless @_ == 1;
  return Catacomb::Field->binpoly($_[0]);
}

sub binnormfield {
  croak("Usage: Catacomb::GF::binnormfield(p, beta)") unless @_ == 2;
  return Catacomb::Field->binnormfield($_[0], $_[1]);
}

sub binpolygroup {
  croak("Usage: Catacomb::GF::binpolygroup(p, g, q)") unless @_ == 3;
  return Catacomb::Group->binary(@_);
}

sub mod { (&div($_[0], $_[1]))[1]; }

sub pow {
  croak("Usage: Catacomb::GF::pow(a, b)") unless @_ == 2;
  my ($a, $b) = @_;
  my $r = Catacomb::GF->new(1);
  while ($b) {
    $r *= $a if $b & 1;
    $a = sqr($a);
    $b >>= 1;
  }
  return $r;
}

sub _binop {
  my ($func, $a, $b, $flag) = @_;
  return new(undef, $flag ? &$func($b, $a) : &$func($a, $b));
}

sub modexp {
  croak("Usage: Catacomb::GF::modexp(p, g, x)") unless @_ == 3;
  my ($p, $g, $x) = @_;
  my $r = Catacomb::GF::Reduce->new($p);
  $g = $r->reduce($g);
  return $r->exp($g, $x);
}

sub modinv {
  croak("Usage: Catacomb::GF::modinv(p, g)") unless @_ == 3;
  my ($g, undef, $i) = gcd($_[0], $_[1]);
  croak("Arguments aren't coprime in Catacomb::GF::modinv") unless $g == 1;
  return $i;
}

#----- That's all, folks ----------------------------------------------------
