
module Kakezan

    def flatten2
	r = MultiNode.new()
	each do |child|
	    case child
	    when MultiNode
		r.append child
	    when MulNode
		r.append child.flatten2
	    when ContainerNode
		r.append child.flatten2
	    else
		r.append child
	    end 
	end
	r
    end

    def name
	n = nil
	for c in @children
	    next if NumberNode === c
	    na = c.name
	    if n.nil?
		n = na
	    else
		raise "multiple names found" if na != n
	    end
	end
	n = "1" if n.nil?
	n
    end

    def factor
	f = 1
	for c in @children
	    f *= c.factor
	end
	f
    end

end

class MulNode < ContainerNode

    include BinaryNode
    include Kakezan

    def initialize(lhs, rhs)
	@lhs, @rhs = lhs, rhs
    end

    def to_s
	lhs = @lhs.to_s
	rhs = @rhs.to_s
	if (/\d$/ =~ lhs && /^\w/ =~ rhs) then
	    "#{lhs} #{rhs}"
	else
	    "#{lhs}.#{rhs}"
	end
    end

end

class MultiNode < ContainerNode

    include Kakezan

    def initialize(*children)
	@children = children
	for c in @children
	    raise "# MultiNode.new(#{children.inspect})" unless Node === c
	end
    end

    def to_s
	s = @children.join(';')
	s.gsub(/\d;\w/) { |dsw| dsw.sub(/;/, ' ') }.gsub(/;/, '.')
    end

    def each
	@children.each {|child| yield child }
    end

    attr_reader :children
    
    def append(other)
	case other
	when MultiNode
	    @children += other.children
	else
	    @children.push other
	end
    end

    def sort
	table = {}
	for child in self
	    name = child.name
	    if (table.include?(name)) then
		table[name] = table[name].mul_eval(child)
	    else
		table[name] = child
	    end
	end
	list = []
	for name in table.keys.sort
	    candi = table[name]
	    if PowNode === candi and NumberNode === candi.lhs then
		v = candi.value
		list.push NumberNode.new(v) unless v == 1
		next
	    end
	    next if candi.power.value == 0
	    list.push candi
	end
        if list.length > 1
	  list.delete(NumberNode::UNITY)
	end
	self.class.new(*list)
    end

    def collect_hash(stopper, op)
	list = []
	for child in self
	    list.push(child.send(op, stopper))
	end
	self.class.new(*list).flatten2
    end

    def expand(stopper)
	collect_hash(stopper, :expand)
    end

    def unalias(stopper)
	collect_hash(stopper, :unalias)
    end

    def foldnumber(stopper)
	collect_hash(stopper, :foldnumber)
    end

    def value
	raise "this is dimensional units" if (@children.size > 1)
	@children.first ? @children.first.value : NumberNode::UNITY.value
    end

end
