# Multi-index class for MPolymial
#
#   by Shin-ichiro Hara
#
# Version 2.00 (2002.02.04)


module Algebra
  class MIndex
    include Enumerable

    module Lex
      def <=> (other)
	@body <=> other.to_a
      end
    end     
  
    module Grlex
      def <=> (other)
	s = (totdeg <=> other.totdeg)
	return s unless s.zero?
	@body <=> other.to_a
      end
    end

    module Grevlex
      def <=> (other)
	s = (totdeg <=> other.totdeg)
	return s unless s.zero?
	n = [size, other.size].max
	(n-1).downto 0 do |i|
	  x = other[i] - self[i]
	  return x unless x.zero?
	end
	0
      end
    end

    module V_lex
      def <=> (other)
#	n = [size, other.size].max
#	0.upto (n-1) do |i|
	V_ORDER.each do |i|
#	  x = self[V_ORDER[i]] - other[V_ORDER[i]]
	  x = self[i] - other[i]
	  return x unless x.zero?
	end
#	p [111111111, self, other]
	0
      end
    end
    
    module V_grlex
      def <=> (other)
	s = (totdeg <=> other.totdeg)
	return s unless s.zero?
#	n = [size, other.size].max
#	0.upto (n-1) do |i|
	V_ORDER.each do |i|
#	  x = self[V_ORDER[i]] - other[V_ORDER[i]]
	  x = self[i] - other[i]
	  return x unless x.zero?
	end
	0
      end
    end
    
    module V_grevlex
      def <=> (other)
	s = (totdeg <=> other.totdeg)
	return s unless s.zero?
#	n = [size, other.size].max
#	(n-1).downto 0 do |i|
	V_ORDER.reverse_each do |i|
#	  x = other[V_ORDER[i]] - self[V_ORDER[i]]
	  x = other[i] - self[i]
	  return x unless x.zero?
	end
	0
      end
    end

    def initialize(array = [])
      @body = array
    end

    def to_a
      @body
    end
    
    def empty?
      @body.empty?
    end

    def size
      @body.size
    end

    def each
      @body.each do |x|
	yield x
      end
    end

    def ==(other)
      @body == other.to_a
    end

    def eql?(other)
      @body.eql? other.to_a
    end

    def hash
      @body.hash
    end

    Unity = new

    def self.[](*ind)
      new(ind)
    end

    def unity
      Unity.dup
    end
  
    def unity?
      self == Unity
    end

    def [](i)
      @body[i] || 0
    end
    
    def []=(i, x)
      k = (self[i] = x)
      if totdeg == 0
	raise "illegal operation"
      end
      k
    end
        
    def self.monomial(idx, height = 1)
      ind0 = []
      (0..idx).each do |i|
	ind0.push(i == idx ? height : 0)
      end
      new(ind0)
    end
    
    def multideg
      @body
    end
    
    def devide?(other)
      each_with_index do |x, i|
	return false if x > other[i]
      end
      true
    end
    
    def devide_or?(other0, other1)
      each_with_index do |x, i|
	return false if x > other0[i] or x > other1[i]
      end
      true
    end
    
    def prime_to?(other)
      each_with_index do |x, i|
	return false if x > 0 && other[i] > 0
      end
      true
    end
    
    def totdeg
      s = 0
      each do |n| s += n; end
      s
    end
    
    def ==(other)
      0.upto [size, other.size].max - 1 do |i|
	return false if self[i] != other[i]
      end
      true
    end
    
    def +(other)
      self.class.new( (0 ... [size, other.size].max).collect{|i|
		 self[i] + other[i]
	       } )
    end
    
    def -(other)
      self.class.new( (0 ... [size, other.size].max).collect{|i|
		 x = self[i] - other[i]
		 raise "#{self} is not devided by #{other}" if x < 0
		 x
	       }).compact!
    end
    
    def annihilate(at)
      self - self.class.monomial(at, self[at])
    end

    def lcm(other)
      self.class.new( (0 ... [size, other.size].max).collect{|i|
		 [self[i], other[i]].max
	       })
    end
    
    def gcm(other)
      (self.class.new( (0 ... [size, other.size].max).collect{|i|
		  [self[i], other[i]].min
		})).compact!
    end
    
    def compact! # be careful, when this index is used plural places!!
      i = size - 1
      i -= 1 while i >= 0 && self[i].zero?
      @body.slice!((i+1)..(-1)) if i < size - 1
      self
    end
    
    def compact # more safe
      dup.compact!
    end
    
    def dup
      self.class.new(@body)
    end
    
    def to_s!(vars = nil, pr = nil, po = nil)
      return @body.inspect unless vars
      a = ""
      each_with_index do |n, i|
	case n
	when 0
	else
#          u = n == 1 ? vars[i].to_s : vars[i].to_s + "#{po}#{n}"
          u = if n == 1
                vars[i].to_s
              elsif /^\}/ =~ po
                "{#{vars[i]}#{po}#{n}"
              else
                "#{vars[i]}#{po}#{n}"
              end
          a.concat(a.empty? ? u : "#{pr}#{u}")
	end
      end
      a
    end
    
    def to_s(var = nil, pr = nil, po = nil)
      $DEBUG ? @body.inspect.gsub(/\s+/, '') : to_s!(var, pr, po)
    end
    
    def inspect(var = nil, po = nil)
      to_s(var, po)
    end
    
    def self.set_ord(ord)
      mod = get_module(ord)
      adopt_module(mod)
    end

    def self.set_V_ORDER(v_ord)
      const_set("V_ORDER", v_ord)
    end

    def self.set_v_ord(ord, v_ord)
      set_V_ORDER(v_ord)
      vord = get_module(ord, v_ord)
      adopt_module(vord)
    end

    def self.get_module(ord, v_ord = nil)
      ord = ord.id2name
      ord = "V_" + ord if v_ord
      eval(ord.capitalize)
    end
  end
end


syntax highlighted by Code2HTML, v. 0.9.1