summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2011-08-29 14:49:07 +0100
committerdakkar <dakkar@thenautilus.net>2011-08-29 14:49:07 +0100
commit24dedb2618abc80efc2af10de597d1debbeba69f (patch)
tree9a5055ded078239f56046695179dcbe2215706cb
parentnotches (diff)
downloadEnigmatic-24dedb2618abc80efc2af10de597d1debbeba69f.tar.gz
Enigmatic-24dedb2618abc80efc2af10de597d1debbeba69f.tar.bz2
Enigmatic-24dedb2618abc80efc2af10de597d1debbeba69f.zip
crypto train, with some stepping
-rw-r--r--lib/Enigmatic/CryptTrain.pm86
-rw-r--r--t/stepping.t31
2 files changed, 117 insertions, 0 deletions
diff --git a/lib/Enigmatic/CryptTrain.pm b/lib/Enigmatic/CryptTrain.pm
new file mode 100644
index 0000000..06a11d2
--- /dev/null
+++ b/lib/Enigmatic/CryptTrain.pm
@@ -0,0 +1,86 @@
+package Enigmatic::CryptTrain;
+use DAKKAR::p 'class';
+use Enigmatic::Types qw(Letter RotorPos);
+use MooseX::Types::Moose qw(ArrayRef);
+use Moose::Util::TypeConstraints;
+
+has rotors => (
+ isa => ArrayRef[class_type('Enigmatic::Rotor')],
+ traits => ['Array'],
+ writer => 'set_rotors',
+ handles => {
+ set_rotor => 'set',
+ rotor_at => 'get',
+ rotors => 'elements',
+ },
+);
+
+sub rotor_count {
+ my ($self) = @_;
+
+ return scalar $self->rotors;
+}
+
+has reflector => (
+ isa => class_type('Enigmatic::Reflector'),
+ writer => 'set_reflector',
+ reader => 'reflector',
+);
+
+has positions => (
+ isa => ArrayRef[RotorPos],
+ traits => ['Array'],
+ writer => 'set_positions',
+ handles => {
+ set_position => 'set',
+ position_at => 'get',
+ positions => 'elements',
+ },
+);
+
+sub map {
+ my $self = shift;
+ my ($letter) = pos_validated_list(
+ \@_,
+ { isa => Letter },
+ );
+
+ for my $element (
+ $self->rotors,
+ $self->reflector,
+ (reverse $self->rotors),
+ ) {
+ $letter = $element->map($letter);
+ }
+ $self->step_positions();
+
+ return $letter;
+}
+
+sub _inc_position {
+ my ($self,$idx) = @_;
+
+ my $cur = $self->position_at($idx);
+ $cur = ($cur+1)%26;
+ $self->set_position($idx,$cur);
+ return $cur;
+}
+
+sub rotor_window_at {
+ my ($self,$idx) = @_;
+
+ return ['A'..'Z']->[$self->position_at($idx)]
+}
+
+sub step_positions {
+ my ($self) = @_;
+
+ for my $pos (0 .. $self->rotor_count -1) {
+ $self->_inc_position($pos);
+ my $rot = $self->rotor_at($pos);
+ last
+ unless $rot->has_notch_at($self->rotor_window_at($pos));
+ }
+
+ return;
+}
diff --git a/t/stepping.t b/t/stepping.t
new file mode 100644
index 0000000..f50bce9
--- /dev/null
+++ b/t/stepping.t
@@ -0,0 +1,31 @@
+#!perl
+use DAKKAR::p 'test';
+
+use Enigmatic::Rotor;
+use Enigmatic::Reflector;
+use Enigmatic::CryptTrain;
+
+subtest 'first position' => sub {
+ my @in = 'A'..'Z';
+
+ my @rots = map { Enigmatic::Rotor->new(@in->join) } 1..3;
+ my $refl = Enigmatic::Reflector->new(@in->join);
+ my $train = Enigmatic::CryptTrain->new({
+ rotors => \@rots,
+ reflector => $refl,
+ positions => [0,0,0],
+ });
+
+ my $pos=0;
+ for my $c ('A' .. 'Z') {
+ is($train->map($c),
+ $c,
+ "identity train on $c");
+ ++$pos;$pos%=26;
+ is_deeply([$train->positions],
+ [$pos,0,0],
+ 'no notches, only 1st moved');
+ }
+};
+
+done_testing();