# Finite Group
#
# by Shin-ichiro Hara
#
# Version 0.9 (2002.03.12)
require "algebra/finite-set"
require "algebra/prime-gen"
module Algebra
module OperatorDomain
def right_act(other)
cast.product(other.cast) {|x, y| x * y}
# product(other, self.class[unity]) {|x, y| x * y}
end
alias act right_act
def left_act(other)
other.right_act(self)
end
def right_quotient(other)
s = Set.phi
remain = cast.dup
while !remain.empty?
x = remain.shift
t = Set[x].right_act(other)
s.push t
remain -= t
end
s
end
alias quotient right_quotient
alias right_coset right_quotient
alias coset right_quotient
def left_quotient(other)
s = Set.phi
remain = cast.dup
while !remain.empty?
x = remain.shift
t = Set[x].left_act(other)
s.push t
remain -= t
end
s
end
alias left_coset left_quotient
def right_representatives(other)
s = Set.phi
remain = cast.dup
while !remain.empty?
x = remain.shift
t = self.class[x].act(other)
s.push x
remain -= t
end
s
end
alias representatives right_representatives
def left_representatives(other)
s = Set.phi
remain = cast.dup
while !remain.empty?
x = remain.shift
t = self.class[x].left_act(other)
s.push x
remain -= t
end
s
end
def right_orbit!(other)
news = Set.phi
while true
each_product(other) do |x, y|
z = x * y
unless include?(z) || news.include?(z)
news.push z
end
end
if news.empty?
break
else
concat news
news = Set.phi
end
end
self
end
alias orbit! right_orbit!
def left_orbit!(other)
other.right_orbit!(self)
end
end
class Set
include OperatorDomain
alias * act
alias / quotient
alias % representatives
def increasing_series(x = unit_group)
a = []
loop do
a.push x
if x >= (y = yield x)
break
end
x = y
end
a
end
def decreasing_series(x = self)
a = []
loop do
a.push x
if x <= (y = yield x)
break
end
x = y
end
a
end
end
class Group < Set
# include OperatorDomain
attr_reader :unity
def quotient_group(u)
# raise "devided by non normal sub group" unless normal?(u)
QuotientGroup.new(self, u)
end
def separate(&b)
self.class.new(unity, *find_all(&b))
end
def initialize(u, *a)
super(u, *a)
@unity = u
end
attr_reader :unity
def unit_group
self.class.new(@unity)
end
def semi_complete!
orbit!(self)
end
def semi_complete
dup.semi_complete!
end
def complete!
concat collect{|x| x.inverse}
orbit!(self)
end
def complete
dup.complete!
end
def self.generate_strong(u, *ary_of_gens)
unless a = ary_of_gens.shift
return new(u)
end
a.first != u and (a = [u] + a)
while b = ary_of_gens.shift
b.first != u and (b = [u] + b)
c = []
a.each do |x|
b.each do |y|
c.push x * y
end
end
a = c
end
new(*a)
end
def closed?
each do |x|
return false unless include? x.inverse
each do |y|
return false unless include?(x * y)
end
end
true
end
def subgroups(&b)
e = unit_group
if block_given?
yield e
end
subgrs = Set[e, self]
_subgroups(e, subgrs, &b)
if block_given?
yield self
end
subgrs
end
def _subgroups(h, subgrs, &b)
while true
new_elem = false
((cast-h).representatives(h)).each do |a|
i = h.append(a).complete!
next if i.size == size
unless subgrs.include? i
new_elem = true
subgrs.push i
if block_given?
yield i
end
if defined?(Primes) and Primes.include?(size/i.size)
else
_subgroups(i, subgrs, &b)
end
end
end
break unless new_elem
end
end
private :_subgroups
def centralizer(s) # C_{self}(s) not C_{s}(self) !!!!
separate{|x| s.all?{|y| x * y == y * x}}
end
def center # C_{self}(self)
centralizer(self)
end
def center?(x) # self == C_{self}(x)
all?{|y| x * y == y * x}
end
def normalizer(s)
separate{|x| s.right_act(Set[x]) == s.left_act(Set[x])}
end
def normal?(s)
all?{|x| s.right_act(Set[x]) == s.left_act(Set[x])}
end
def normal_subgroups
s = Set.phi
subgroups.each{|x|
if x.size == 1 or
x.size == size or
normal?(x)
yield x if block_given?
s.push x
end
}
s
end
def conjugacy_class(x)
(self % centralizer(Set[x])).map_s{|y| x.conjugate(y)}
end
def conjugacy_classes
s = Set.phi
t = cast
until t.empty?
x = conjugacy_class(t.pick)
if block_given?
yield x
end
s.push x
t -= x
end
s
end
def simple?
subgroups.all?{|x|
x.size == 1 or x.size == size or
!normal?(x)
}
end
def commutator(h = self)
gr = unit_group
each do |x|
h.each do |y|
gr.push x.inverse * y.inverse * x * y
end
end
self == h ? gr.semi_complete! : gr.complete!
end
def D(n = 1)
if n == 0
self
else
D(n - 1).commutator D(n-1)
end
end
def commutator_series
decreasing_series do |s|
s.D
end
end
def solvable?
commutator_series.last.size == 1
end
def K(n = 1)
if n == 0
self
else
commutator K(n-1)
end
end
def descending_central_series
decreasing_series do |s|
commutator s
end
end
def Z(n = 1)
if n == 0
unit_group
else
separate{ |x|
commutator(Set[x]) <= Z(n-1)
}
end
end
def ascending_central_series
increasing_series do |s|
s.Z
end
end
def nilpotent?
descending_central_series.last.size == 1
#ascending_central_series.last.size == size
end
def nilpotency_class
a = descending_central_series
if a.last.size == 1
a.size - 1
else
nil
end
end
def to_a
[unity, *(super - [unity])]
end
end
#will be optimized
class QuotientGroup < Group
def initialize(univ, u)
@univ = univ
super(u, *univ.quotient(u))
end
def inverse
map_s{|x| x.inverse}
end
alias inv inverse
end
end
if __FILE__ == $0
end
syntax highlighted by Code2HTML, v. 0.9.1