{
our
$VERSION
=
'0.004'
; }
'/'
=>
'divided_by'
,
'=='
=>
'equal_to'
,
'eq'
=>
'equal_to'
,
'-'
=>
'minus'
,
'!='
=>
'not_equal_to'
,
'+'
=>
'plus'
,
'""'
=>
'stringify'
,
'*'
=>
'times'
,
'**'
=>
'to_power'
;
has
field
=> (
is
=>
'ro'
,
required
=> 1,
isa
=>
sub
{
my
$F
=
shift
;
(blessed(
$F
) &&
$F
->isa(
'Math::GF'
))
or ouch 500,
'field is not a valid Math::GF instance'
;
$F
->order_is_prime
or ouch 500,
'cannot build Zn over non-prime n'
;
return
1;
},
);
has
n
=> (
is
=>
'ro'
,
init_arg
=>
undef
,
lazy
=> 1,
default
=>
sub
{
shift
->field->order },
);
has
v
=> (
is
=>
'ro'
,
default
=> 0,
);
has
i
=> (
is
=>
'ro'
,
lazy
=> 1,
builder
=>
'BUILD_multiplicative_inverse'
,
);
has
o
=> (
is
=>
'ro'
,
lazy
=> 1,
builder
=>
'BUILD_additive_inverse'
,
);
sub
assert_compatibility {
my
(
$self
,
$other
) =
@_
;
(blessed(
$other
) &&
$other
->isa(
'Math::GF::Zn'
))
|| ouch 500,
'one of the operands is not a Math::GF::Zn object'
;
my
$n
=
$self
->n;
$n
==
$other
->n
|| ouch 500,
'the two operands are not in the same field'
;
return
$n
;
}
sub
BUILD_multiplicative_inverse {
my
$self
=
shift
;
my
$v
=
$self
->v or ouch 500,
'no inverse for 0'
;
my
$n
=
$self
->n;
my
(
$i
,
$x
) = (1, 0);
for
(1 ..
$n
- 1) {
$x
= (
$x
+
$v
) %
$n
;
return
$_
if
$x
== 1;
}
ouch 500,
"no inverse for $v in Z_$n"
;
}
sub
BUILD_additive_inverse {
my
$self
=
shift
;
return
$self
->n -
$self
->v;
}
sub
divided_by {
my
(
$self
,
$other
,
$swap
) =
@_
;
my
$n
=
$self
->assert_compatibility(
$other
);
(
$self
,
$other
) = (
$other
,
$self
)
if
$swap
;
return
$self
->new(
field
=>
$self
->field,
v
=> ((
$self
->v *
$other
->i) %
$n
),
);
}
sub
equal_to {
my
(
$self
,
$other
,
$swap
) =
@_
;
my
$n
=
$self
->assert_compatibility(
$other
);
return
$self
->v ==
$other
->v;
}
sub
inv {
my
$self
=
shift
;
return
$self
->new(
field
=>
$self
->field,
v
=>
$self
->i,
i
=>
$self
->v,
);
}
sub
minus {
my
(
$self
,
$other
,
$swap
) =
@_
;
my
$n
=
$self
->assert_compatibility(
$other
);
return
$self
->new(
field
=>
$self
->field,
v
=> ((
$self
->v -
$other
->v) %
$n
),
);
}
sub
not_equal_to {
return
!
shift
->equal_to(
@_
);
}
sub
opp {
my
$self
=
shift
;
return
$self
->new(
field
=>
$self
->field,
v
=>
$self
->o,
o
=>
$self
->v,
);
}
sub
plus {
my
(
$self
,
$other
,
$swap
) =
@_
;
my
$n
=
$self
->assert_compatibility(
$other
);
return
$self
->new(
field
=>
$self
->field,
v
=> ((
$self
->v +
$other
->v) %
$n
),
);
}
sub
stringify {
return
shift
->v;
}
sub
times
{
my
(
$self
,
$other
,
$swap
) =
@_
;
my
$n
=
$self
->assert_compatibility(
$other
);
return
$self
->new(
field
=>
$self
->field,
v
=> ((
$self
->v *
$other
->v) %
$n
),
);
}
sub
to_power {
my
(
$self
,
$exp
,
$swap
) =
@_
;
ouch 500,
'cannot elevate'
if
$swap
;
my
(
$n
,
$v
,
$x
) = (
$self
->n,
$self
->v, 1);
while
(
$exp
> 0) {
$x
= (
$x
*
$v
) %
$n
or
last
;
$exp
--;
}
return
$self
->new(
field
=>
$self
->field,
v
=>
$x
,
);
}
1;