The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# This file is part of CM-Permutation
#
# This software is copyright (c) 2011 by Stefan Petrea.
#
# This is free software; you can redistribute it and/or modify it under
# the same terms as the Perl 5 programming language system itself.
#
use strict;
use warnings;
package CM::Morphism;
{
  $CM::Morphism::VERSION = '0.94';
}
use strict;
use warnings;
use Moose;
use Moose::Util::TypeConstraints;
use List::AllUtils qw/all uniq/;

=pod

=head1 NAME

CM::Morphism - This module describes a group homomorphism


=head1 VERSION

version 0.94

=head1 SEE ALSO

L<http://en.wikipedia.org/wiki/Group_homomorphism>


=cut



use overload '*' => 'composition';

type 'Group'
	=> where {
		$_->does('CM::Group'); # $_ does the role CM::Group
	};



# this will be a group homomorphism which we'll prove 
# step by step to be an epimorphism or a monomorphism
has f => (
	isa      => 'CodeRef',
	is       => 'rw',
	required => 1,
);


# can I write regexes instead of isas ? 

has domain => (
	isa      => 'Group',
	is       => 'rw',
	required => 1,
);



has codomain => (
	isa      => 'Group',
	is       => 'rw',
	required => 1,
);


# prove that this is indeed a morphism
sub prove {
	my ($self) = @_;
	my $f = $self->f;

	confess "undefined" if @{$self->domain->elements}==0;

	all {
		my $x = $_;
		all {
			my $y = $_;

			# * means the group operations here not multiplication..
			$f->( $x   *       $y ) ==
			$f->( $x ) * $f->( $y );

		} @{$self->codomain->elements}
	} @{$self->domain->elements};
}


# the kernel and the image are groups themselves so we should create a subgroup of domain and codomain
sub kernel {
	my ($self) = @_;
	
	my $group = $self->domain->meta->name->new({n=>1});
	$group->compute_elements(sub{});

	my @elements = 
	uniq
	grep {
		$self->f->($_) == 
		$self->codomain->identity;
	} @{$self->domain->elements};

	$group->elements(\@elements);


	return $group;
}

sub image {
	my ($self) = @_;

	my $group = $self->codomain->meta->name->new({n=>1});
	$group->compute_elements(sub{});

	my @elements = 
	uniq
	map {
		$self->f->($_);
	} @{$self->domain->elements};


	$group->elements(\@elements);

	return $group;
}


# compose f o g = h <=> f(g(x)) = h(x)
#
#           f
#      G  -----   H
#        \        |
#         \       |  
#          \      |
#           \     |
#      f o g \    |  g
#             \   |
#              \  |
#               \ |
#                 N

sub composition {
	my ($f,$g) = @_;

	return CM::Morphism->new({
			f        =>
			sub { 
				my ($x) = @_;
				return $f->f->( 
					$g->f->( 
						$x
					)
				);
			},
			domain   => $g->domain,
			codomain => $f->codomain,
	});
}

1;