# Multi-index class for MPolymial
#
#   by Shin-ichiro Hara
#
# Version 1.01 (2001.09.10)

#require "cast"

module Algebra
class MIndex < Array
  include Comparable
  @@order = "grevlex"

  alias org_order <=>
#  def org_order(other)
#    if empty?
#      if other.empty?
#	0
#      else
#	1
#      end
#    else
#      self <=> other
#    end
#  end

  Unity = self[]

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

  def [](i)
    super || 0
  end

  def []=(i, x)
    k = super
    if totdeg == 0
      raise "illegal operation"
    end
    k
  end

  def ord_lex(other)
    org_order(other)
  end

  def ord_grlex(other)
    s = (totdeg <=> other.totdeg)
    return s unless s.zero?
    org_order(other)
  end

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

  def self.set_ord(ord)
    ord = ord.id2name if ord.is_a?(Symbol)
    @@order = ord
    eval("module Algebra
            class MIndex# < Array
	      alias <=> ord_#{ord}
	    end
	  end", TOPLEVEL_BINDING)
  end

  def self.order(ord)
    set_ord(ord)
  end

  def self.get_ord
    @@order
  end

  def self.order
    get_ord
  end

  def self.monomial(idx, height = 1)
    ind0 = []
    (0..idx).each do |i|
      ind0.push(i == idx ? height : 0)
    end
    self[*ind0]
  end

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

  def gcm(other)
    (type[* (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?
    slice!((i+1)..(-1)) if i < size - 1
    self
  end

  def compact # more safe
    dup.compact!
  end

  def to_s!(vars = nil, po = nil)
    return 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

  alias inspect! inspect

  def to_s(var = nil, po = nil)
    $DEBUG ? inspect!.gsub(/\s+/, '') : to_s!(var, po)
  end
 
  def inspect(var = nil)
    to_s(var)
  end
end
end
