To compile code performing an arithmetic operation.

§1. This section provides a single function to compile Inter code to perform an arithmetic operation. It implements the {-arithmetic-operation:X:Y} bracing when used in inline invocations, and is also needed for equation solving; see Compile Solutions to Equations. Because of that, the function is called either with X and Y set to values, or with EX and EY set to equation nodes, but not both. eqn is set only for the equations case; but in both cases KX and KY are the kinds of the arithmetic operands, and op is the operation number.

For unary operations, Y, EY and KY will all be NULL.

What happens is straightforward enough, but we provide a fair range of different operations, and we have to manage scaling factors and whether the underlying arithmetic is integer or floating-point.

void Kinds::Compile::perform_arithmetic_emit(int op, equation *eqn,
    parse_node *X, equation_node *EX, kind *KX,
    parse_node *Y, equation_node *EY, kind *KY) {
    int binary = TRUE;
    if (Kinds::Dimensions::arithmetic_op_is_unary(op)) binary = FALSE;
    int use_fp = FALSE, promote_X = FALSE, promote_Y = FALSE, demote_result = FALSE,
        reduce_modulo_1440 = FALSE;
    kind *KR = Kinds::Dimensions::arithmetic_on_kinds(KX, KY, op);
    if ((KX) && (KY)) {
        #ifdef IF_MODULE
        kind *KT = TimesOfDay::kind();
        if ((KT) && (Kinds::eq(KR, KT))) reduce_modulo_1440 = TRUE;
        #endif
        if (((Kinds::FloatingPoint::uses_floating_point(KX)) ||
                (Kinds::FloatingPoint::uses_floating_point(KY)))
            && (Kinds::FloatingPoint::uses_floating_point(KR) == FALSE)
            && ((op == TIMES_OPERATION) || (op == DIVIDE_OPERATION)))
            demote_result = TRUE;
    }
    Choose which form of arithmetic and promotion1.1;
    Optimise promotions from number to real number1.2;
    if (reduce_modulo_1440) {
        EmitCode::call(Hierarchy::find(NUMBER_TY_TO_TIME_TY_HL));
        EmitCode::down();
    }
    if (demote_result) {
        Kinds::FloatingPoint::begin_deflotation_emit(KR);
    }
    switch (op) {
        case EQUALS_OPERATION: Emit set-equals1.11; break;
        case PLUS_OPERATION: Emit plus1.3; break;
        case MINUS_OPERATION: Emit minus1.4; break;
        case TIMES_OPERATION: Emit times1.5; break;
        case DIVIDE_OPERATION: Emit divide1.6; break;
        case REMAINDER_OPERATION: Emit remainder1.7; break;
        case APPROXIMATION_OPERATION: Emit approximation1.8; break;
        case ROOT_OPERATION: Emit root1.9; break;
        case REALROOT_OPERATION: use_fp = TRUE; Emit root1.9; break;
        case CUBEROOT_OPERATION: Emit cube root1.10; break;
        case POWER_OPERATION: Emit a power of the left operand1.13; break;
        case UNARY_MINUS_OPERATION: Emit unary minus1.12; break;
        case IMPLICIT_APPLICATION_OPERATION: Emit implicit application1.14; break;
        default:
            StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible),
                "this doesn't seem to be an arithmetic operation",
                "suggesting a problem with some inline definition.");
            break;
    }
    if (demote_result) Kinds::FloatingPoint::end_deflotation_emit(KR);
    if (reduce_modulo_1440) EmitCode::up();
}

§1.1. For a binary operation, note that "pi plus pi", "pi plus 3", and "3 plus pi" must all use floating-point, whereas "3 plus 3" uses integer arithmetic: in other words, if either operand is real, then real arithmetic must be used. "Promotion" means converting an integer to a real number (I'm not quite sure why that is traditionally thought of as being better) — in "pi plus 3", the integer 3 is promoted to real.

Choose which form of arithmetic and promotion1.1 =

    if (binary) {
        if (Kinds::FloatingPoint::uses_floating_point(KX)) {
            if (Kinds::FloatingPoint::uses_floating_point(KY)) {
                use_fp = TRUE; promote_X = FALSE; promote_Y = FALSE;
            } else {
                use_fp = TRUE; promote_X = FALSE; promote_Y = TRUE;
            }
        } else {
            if (Kinds::FloatingPoint::uses_floating_point(KY)) {
                use_fp = TRUE; promote_X = TRUE; promote_Y = FALSE;
            } else {
                use_fp = FALSE; promote_X = FALSE; promote_Y = FALSE;
            }
        }
    } else {
        if (Kinds::FloatingPoint::uses_floating_point(KX)) {
            use_fp = TRUE; promote_X = FALSE; promote_Y = FALSE;
        } else {
            use_fp = FALSE; promote_X = FALSE; promote_Y = FALSE;
        }
    }

§1.2. Making this optimisation ensures that if X or Y are literal K_number values then they will be converted to literal K_real_number values at compile time rather than at runtime, saving a function call in cases like

    let the magic value be 4 + pi;

where there is no need to convert 4 to 4.0 at runtime; we can simply reinterpret it as a real.

Optimise promotions from number to real number1.2 =

    if ((promote_X) && (Kinds::eq(KX, K_number))) { promote_X = FALSE; KX = K_real_number; }
    if ((promote_Y) && (Kinds::eq(KY, K_number))) { promote_Y = FALSE; KY = K_real_number; }

§1.3. Emit plus1.3 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_PLUS_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::inv(PLUS_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.4. Emit minus1.4 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_MINUS_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::inv(MINUS_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.5. Emit times1.5 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_TIMES_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        Kinds::Scalings::rescale_multiplication_emit_op(KX, KY);
        EmitCode::inv(TIMES_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
        Kinds::Scalings::rescale_multiplication_emit_factor(KX, KY);
    }

§1.6. Emit divide1.6 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_DIVIDE_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::call(Hierarchy::find(INTEGERDIVIDE_HL));
        EmitCode::down();
            Kinds::Scalings::rescale_division_emit_op(KX, KY);
            Emit the X-operand1.3.1;
            Kinds::Scalings::rescale_division_emit_factor(KX, KY);
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.7. Emit remainder1.7 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_REMAINDER_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::call(Hierarchy::find(INTEGERREMAINDER_HL));
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.8. Emit approximation1.8 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_APPROXIMATE_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::call(Hierarchy::find(ROUNDOFFVALUE_HL));
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.9. Emit root1.9 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_ROOT_HL));
        EmitCode::down();
            Emit the X-operand1.3.1;
        EmitCode::up();
    } else {
        EmitCode::call(Hierarchy::find(SQUAREROOT_HL));
        EmitCode::down();
            Kinds::Scalings::rescale_root_emit_op(KX, 2);
            Emit the X-operand1.3.1;
            Kinds::Scalings::rescale_root_emit_factor(KX, 2);
        EmitCode::up();
    }

§1.10. Emit cube root1.10 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_CUBE_ROOT_HL));
        EmitCode::down();
            Emit the X-operand1.3.1;
        EmitCode::up();
    } else {
        EmitCode::call(Hierarchy::find(CUBEROOT_HL));
        EmitCode::down();
            Kinds::Scalings::rescale_root_emit_op(KX, 3);
            Emit the X-operand1.3.1;
            Kinds::Scalings::rescale_root_emit_factor(KX, 3);
        EmitCode::up();
    }

§1.11. Emit set-equals1.11 =

    EmitCode::inv(STORE_BIP);
    EmitCode::down();
        EmitCode::reference();
        EmitCode::down();
            Emit the X-operand1.3.1;
        EmitCode::up();
        if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
        Emit the Y-operand1.3.2;
        if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
    EmitCode::up();

§1.12. Emit unary minus1.12 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_NEGATE_HL));
        EmitCode::down();
            Emit the X-operand1.3.1;
        EmitCode::up();
    } else {
        EmitCode::inv(UNARYMINUS_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
        EmitCode::up();
    }

§1.13. We accomplish integer powers by repeated multiplication. This is partly because Inter has no "to the power of" opcode, partly because the powers involved will always be small, partly because of the need for scaling to come out right.

Emit a power of the left operand1.13 =

    if (use_fp) {
        EmitCode::call(Hierarchy::find(REAL_NUMBER_TY_POW_HL));
        EmitCode::down();
            if (promote_X) Kinds::FloatingPoint::begin_flotation_emit(KX);
            Emit the X-operand1.3.1;
            if (promote_X) Kinds::FloatingPoint::end_flotation_emit(KX);
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        int p = 0;
        if (Y) p = Rvalues::to_int(Y);
        else p = Rvalues::to_int(EY->leaf_constant);
        if (p <= 0) EquationSolver::issue_problem_on_root(eqn, EY);
        else {
            for (int i=1; i<p; i++) {
                Kinds::Scalings::rescale_multiplication_emit_op(KX, KX);
                EmitCode::inv(TIMES_BIP);
                EmitCode::down();
                    Emit the X-operand1.3.1;
            }
            Emit the X-operand1.3.1;
            for (int i=1; i<p; i++) {
                EmitCode::up();
                Kinds::Scalings::rescale_multiplication_emit_factor(KX, KX);
            }
        }
    }

§1.14. This is used in equation solving only; here we are evaluating a mathematical function like log pi, where X is the function (in this case log) and Y the value (in this case pi). Clearly a function cannot be promoted.

Emit implicit application1.14 =

    if (use_fp) {
        EmitCode::inv(INDIRECT1_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
            if (promote_Y) Kinds::FloatingPoint::begin_flotation_emit(KY);
            Emit the Y-operand1.3.2;
            if (promote_Y) Kinds::FloatingPoint::end_flotation_emit(KY);
        EmitCode::up();
    } else {
        EmitCode::inv(INDIRECT1_BIP);
        EmitCode::down();
            Emit the X-operand1.3.1;
            Emit the Y-operand1.3.2;
        EmitCode::up();
    }

§1.3.1. Emit the X-operand1.3.1 =

    if (X) CompileValues::to_code_val_of_kind(X, KX);
    else EquationSolver::compile_enode(eqn, EX);

§1.3.2. Emit the Y-operand1.3.2 =

    if (Y) CompileValues::to_code_val_of_kind(Y, KY);
    else EquationSolver::compile_enode(eqn, EY);