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


module Algebra
  class MIndex
    include Enumerable

    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)
      type.new( (0 ... [size, other.size].max).collect{|i|
		 self[i] + other[i]
	       } )
    end
    
    def -(other)
      type.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 - type.monomial(at, self[at])
    end

    def lcm(other)
      type.new( (0 ... [size, other.size].max).collect{|i|
		 [self[i], other[i]].max
	       })
    end
    
    def gcm(other)
      (type.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
      type.new(@body)
    end
    
    def to_s!(vars = nil, po = nil)
      return @body.inspect unless vars
      a = ""
      each_with_index do |n, i|
	case n
	when 0
	else
	  a.concat(n == 1 ? vars[i].to_s : vars[i].to_s + "#{po}#{n}")
	end
      end
      a
    end
    
    def to_s(var = nil, po = nil)
      $DEBUG ? @body.inspect.gsub(/\s+/, '') : to_s!(var, po)
    end
    
    def inspect(var = nil, po = nil)
      to_s(var, po)
    end
  end
end
