/*
 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.graal.replacements.nodes;

import com.oracle.graal.api.meta.*;
import com.oracle.graal.compiler.common.*;
import com.oracle.graal.compiler.common.type.*;
import com.oracle.graal.graph.*;
import com.oracle.graal.graph.spi.*;
import com.oracle.graal.lir.gen.*;
import com.oracle.graal.nodes.*;
import com.oracle.graal.nodes.calc.*;
import com.oracle.graal.nodes.spi.*;

public class MathIntrinsicNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable {

    @Input private ValueNode x;
    private final Operation operation;

    public enum Operation {
        ABS,
        SQRT,
        LOG,
        LOG10,
        SIN,
        COS,
        TAN
    }

    public ValueNode x() {
        return x;
    }

    public Operation operation() {
        return operation;
    }

    public MathIntrinsicNode(ValueNode x, Operation op) {
        super(StampFactory.forKind(Kind.Double));
        assert x.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(x.stamp()) == 64;
        this.x = x;
        this.operation = op;
    }

    @Override
    public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
        Value input = builder.operand(x());
        Value result;
        switch (operation()) {
            case ABS:
                result = gen.emitMathAbs(input);
                break;
            case SQRT:
                result = gen.emitMathSqrt(input);
                break;
            case LOG:
                result = gen.emitMathLog(input, false);
                break;
            case LOG10:
                result = gen.emitMathLog(input, true);
                break;
            case SIN:
                result = gen.emitMathSin(input);
                break;
            case COS:
                result = gen.emitMathCos(input);
                break;
            case TAN:
                result = gen.emitMathTan(input);
                break;
            default:
                throw GraalInternalError.shouldNotReachHere();
        }
        builder.setResult(this, result);
    }

    public Constant evalConst(Constant... inputs) {
        assert inputs.length == 1;
        return Constant.forDouble(doCompute(inputs[0].asDouble(), operation()));
    }

    @Override
    public Node canonical(CanonicalizerTool tool) {
        if (x().isConstant()) {
            return ConstantNode.forPrimitive(evalConst(x().asConstant()), graph());
        }
        return this;
    }

    @NodeIntrinsic
    public static double compute(double value, @ConstantNodeParameter Operation op) {
        return doCompute(value, op);
    }

    private static double doCompute(double value, Operation op) throws GraalInternalError {
        switch (op) {
            case ABS:
                return Math.abs(value);
            case SQRT:
                return Math.sqrt(value);
            case LOG:
                return Math.log(value);
            case LOG10:
                return Math.log10(value);
            case SIN:
                return Math.sin(value);
            case COS:
                return Math.cos(value);
            case TAN:
                return Math.tan(value);
        }
        throw new GraalInternalError("unknown op %s", op);
    }
}

@@ -18,16 +18,22 @@
 c) 2012,
+ 2014,
  Oracle 
@@ -1227,41 +1227,8 @@
 .*;%0A
-import com.oracle.graal.graph.*;%0A
 impo
@@ -1447,16 +1447,13 @@
 nds 
-Floating
+Unary
 Node
@@ -1467,25 +1467,8 @@
 ents
- Canonicalizable,
  Ari
@@ -1494,40 +1494,8 @@
  %7B%0A%0A
-    @Input private ValueNode x;%0A
     
@@ -1662,60 +1662,8 @@
  %7D%0A%0A
-    public ValueNode x() %7B%0A        return x;%0A    %7D%0A%0A
     
@@ -1765,17 +1765,21 @@
 lueNode 
-x
+value
 , Operat
@@ -1835,16 +1835,23 @@
 .Double)
+, value
 );%0A     
@@ -1860,17 +1860,21 @@
  assert 
-x
+value
 .stamp()
@@ -1922,17 +1922,21 @@
 getBits(
-x
+value
 .stamp()
@@ -1948,28 +1948,8 @@
 64;%0A
-        this.x = x;%0A
     
@@ -2119,17 +2119,24 @@
 operand(
-x
+getValue
 ());%0A   
@@ -3202,16 +3202,21 @@
  public 
+Value
 Node can
@@ -3244,16 +3244,36 @@
 ool tool
+, ValueNode forValue
 ) %7B%0A    
@@ -3280,19 +3280,24 @@
     if (
-x()
+forValue
 .isConst
@@ -3364,11 +3364,16 @@
 nst(
-x()
+forValue
 .asC
@@ -3386,17 +3386,8 @@
 t())
-, graph()
 );%0A 
@@ -3639,34 +3639,8 @@
  op)
- throws GraalInternalError
  %7B%0A 
@@ -4100,34 +4100,53 @@
 value);%0A        
-%7D%0A
+    default:%0A        
         throw ne
@@ -4184,20 +4184,30 @@
 p %25s%22, op);%0A
+        %7D%0A
     %7D%0A%7D%0A

make MathIntrinsicNode a UnaryNode