# Algebraic System Library
#
#   by Shin-ichiro Hara
#
# Version 1.1 (2001.04.20)

require "algebra/numeric-supplement"
require "algebra/auto-require"

module Algebra
  module AlgebraCreator
    def create(ground)
      klass = Class.new(self)
      klass.sysvar :ground, ground
      def klass.inspect
	to_s
      end
      def klass.to_s
	s = super
	s = "(#{superclass.inspect}/#{ground.inspect})" if s =~ /^#/ #/
	s.gsub(/Algebra::/ , '')
      end
      klass
    end

    # Needed in the type conversion of MatrixAlgebra
    def wedge(otype) # =:= tensor
      if superior?(otype)
        self
      elsif otype.respond_to?(:superior?) && otype.superior?(self)
        otype
      else
        raise "wedge: unknown pair (#{self}) .wedge (#{otype})"
      end
    end

    def superior?(otype)
      if otype <= Numeric || self <= otype
        true
      elsif otype.respond_to?(:ground)
        superior?(otype.ground)
      else
        false
      end
    end

    def sysvar(var_name, value = nil, sw = false)
      var_name = var_name.id2name if var_name.is_a?(Symbol)
      
      class_eval <<__END_OF_CLASS_DEFINITION__
        @@#{var_name} = nil
        def self.#{var_name}; @@#{var_name}; end
        def self.#{var_name}=(value); @@#{var_name} = value; end
__END_OF_CLASS_DEFINITION__

      send(var_name + "=", value) if value

      if sw
      class_eval <<__END_OF_CLASS_DEFINITION__
        def #{var_name}; @@#{var_name}; end
        def #{var_name}=(value); @@#{var_name} = value; end
__END_OF_CLASS_DEFINITION__
      end
    end
  end
      
  module AlgebraBase
    def zero; self.class.zero; end
    def unity; self.class.unity; end
    
    def zero?; zero == self; end
    def unit?; unity == self or -unity == self; end
    def unity?; unity == self; end
    
    def ground; self.class.ground; end
    def ground=(bf); self.class.ground = bf; end
    def devide?(other)
      if self.class.field?
	true
      elsif self.class.euclidian?
	q, r = other.divmod(self)
	r.zero?
      else
	raise "don't konw #{self} divides #{other}"
      end
    end

    def regulate(x)
      self.class.regulate(x)
    end
    #  private :regulate
    
    def self.append_features(klass)
      def klass.field?
	!method_defined?(:divmod) # may be overwrited
      end
      
      def klass.euclidian?
	method_defined?(:divmod) #  may be overwrited
      end

      def klass.ufd?
	euclidian? #  may be overwrited
      end
      
      def klass.zero; new(ground.zero); end
      
      def klass.unity; new(ground.unity); end
      
      def klass.regulate(x)
	if x.is_a? self
	  x
	elsif y = ground.regulate(x)
	  new(y)
	else
	  nil
	end
      end
      super
    end
    
  # Operations
  
    def +@
      self
    end
    
    def -@
      zero - self
    end
    
    def ==(other)
      if o = regulate(other)
	yield o
      else
	x , y = other.coerce(self)
	x == y
      end
    end
    
    def +(other)
      if o = regulate(other)
	yield o
      else
	x , y = other.coerce(self)
	x + y
      end
    end
    
    def -(other)
      if o = regulate(other)
	yield o
      else
	x , y = other.coerce(self)
	x - y
      end
    end
    
    def *(other)
      if o = regulate(other)
	yield o
      else
	x , y = other.coerce(self)
	x * y
      end
    end
    
    def /(other)
      if o = regulate(other)
	yield o
      else
	x , y = other.coerce(self)
	x / y
      end
    end
    
    def **(n)
      if ! n.is_a? Integer or n < 0
	raise "index must be non negative integer"
      elsif n == 0
	return unity
      elsif n == 1
	self
      else
	q , r = n.divmod 2
	x = self ** q
	x = x * x
	x = x * self if r > 0
	x
      end
    end
    
    alias ^ **
      
    def coerce(other)
      if x = regulate(other)
	[x, self]
      else
	raise "(ALG.SYS) can't coerce: (#{self.class}).coerce(#{other.class}) : (#{self}).coerce(#{other})"
	  end
    end
  end
end
    
