class Quaternion {

double    scalar;
double[]  vector;

Quaternion(){
     // initialize me, if no initializing parameters
     scalar = 1;
     vector = new double[]{0,0,0};
}

Quaternion(String axis, double alpha){
     // initialize me as a rotator around X,Y or Z, by alpha degrees
     alpha = alpha * Math.PI/180;
     if (axis.equals("X")) vector = new double[]{1,0,0};
     if (axis.equals("Y")) vector = new double[]{0,1,0};
     if (axis.equals("Z")) vector = new double[]{0,0,1};
     vector = scale(Math.sin(alpha/2.0));
     scalar = Math.cos(alpha/2.0);
}

public Quaternion add(Quaternion q){
     // add a quaternion q to me, returning a quaternion
     Quaternion result = new Quaternion();
     result.scalar = scalar + q.scalar;
     result.vector = add(q.vector);
     return result;
}

public Quaternion subtract(Quaternion q){
     // subtract a quaternion q from me, returning a quaternion
     Quaternion result = new Quaternion();
     result.scalar = scalar - q.scalar;
     result.vector = subtract(q.vector);
     return result;
}

public Quaternion mult(Quaternion q){
     // multiply a quaternion q by me, returning a quaternion
     Quaternion result = new Quaternion();
     result.scalar = scalar * q.scalar - dot(q.vector);
     result.vector = add(add(scale(q.vector),scale(q.scalar)),cross(q.vector));
     return result;
}

public Quaternion mult(double[] v){
     // multiply a vector v by me, returning a quaternion
     Quaternion result = new Quaternion();
     result.scalar = -dot(v);
     result.vector = add(add(scale(v),scale(0.0)),cross(v));
     return result;
}

public Quaternion inv(){
     // invert me, returning a quaternion
     Quaternion result = new Quaternion();
     double normq2 = scalar * scalar + dot(vector);
     result.scalar = (1/normq2) * scalar;
     result.vector = scale(-(1/normq2));
     return result;
}

public Quaternion conj(){
     // return my conjugate, a quaternion
     Quaternion result = new Quaternion();
     result.scalar = scalar;
     result.vector = new double[]{-vector[0],-vector[1],-vector[2]};
     return result;
}

public Quaternion abs(){
     // return |q|, a quaternion with a 0 vector part
     Quaternion result = new Quaternion();
     result.scalar = Math.pow(scalar * scalar + dot(vector),0.5);
     result.vector = new double[]{0.0,0.0,0.0};
     return result;
}

public Quaternion norm(){
     // return |q||q|, a quaternion with a 0 vector part
     Quaternion result = new Quaternion();
     result.scalar = scalar * scalar + dot(vector);
     result.vector = new double[]{0.0,0.0,0.0};
     return result;
}

public void output(){
     // list my scalar and vector parts to the standard output device
     System.out.println(" scalar= "+scalar +
                        " vector= ["+vector[0]+", "+vector[1]+", "+vector[2]+"]");
}

// other trig and logarithmic methods (applied to self) omitted for brevity

// the rest of my methods are for internal use only...

private double[] add(double[] v){
    //add my vector to a passed vector, returning a vector
    return new double[]{vector[0]+v[0],vector[1]+v[1],vector[2]+v[2]};
}

private double[] subtract(double[] v){
    //add my vector to a passed vector, returning a vector
    return new double[]{vector[0]-v[0],vector[1]-v[1],vector[2]-v[2]};
}

private double[] add(double[] v1, double[] v2){
    //add any two passed vectors, returning a vector
    return new double[]{v1[0]+v2[0],v1[1]+v2[1],v1[2]+v2[2]};
}

private double[] scale(double s){
    //scale my vector by the passed scalar, returning a vector
    return new double[]{s*vector[0],s*vector[1],s*vector[2]};
}

private double[] scale(double[] v){
    //multiply a passed vector by my scalar, returning a vector
    return new double[]{scalar*v[0],scalar*v[1],scalar*v[2]};
}

private double dot(double[] v){
    //dot my vector with a passed vector, returning a scalar
    return vector[0]*v[0]+vector[1]*v[1]+vector[2]*v[2];
}

private double[] cross(double[] v){
    //cross my vector with a passed vector, returning a vector
    return new double[]{vector[1]*v[2]-vector[2]*v[1],
                        vector[2]*v[0]-vector[0]*v[2],
                        vector[0]*v[1]-vector[1]*v[0]};
}

}

