/*
**  CUBIC.C - Solve a cubic polynomial
**  public domain by Ross Cottrell
*/

/*

  This program is part of the TACLeBench benchmark suite.
  Version V 1.9

  Name: cubic

  Author: Ross Cottrell

  Function: cubic solves some cubic functions

  Source: MiBench
          http://wwweb.eecs.umich.edu/mibench

  Original name: basicmath_small

  Changes: no major functional changes

  License: this code is FREE with no restrictions

*/

#include "snipmath.h"
#include "wcclibm.h"

/*
  Forward declaration of functions
*/

// Wasm loop bounds


#include "wcclibm.c"


__attribute__((import_module("__pragma"), import_name("loopbound"))) extern void
__pragma_loopbound(unsigned int min_bound, unsigned int max_bound);

__attribute__((always_inline)) static inline void
cubic_solveCubic(float a, float b, float c, float d, int *solutions, float *x);
__attribute__((noinline)) __attribute__((export_name("entrypoint")))
__attribute__((noinline)) __attribute__((export_name("entrypoint"))) void
cubic_main(void);
__attribute__((always_inline)) static inline void cubic_init(void);
__attribute__((always_inline)) static inline int cubic_return(void);
__attribute__((noinline)) __attribute__((export_name("main")))
__attribute__((noinline)) __attribute__((export_name("main"))) int
main(void);

/*
  Declaration of global variables
*/

float cubic_a1, cubic_b1, cubic_c1, cubic_d1;
float cubic_a2, cubic_b2, cubic_c2, cubic_d2;
float cubic_a3, cubic_b3, cubic_c3, cubic_d3;
float cubic_a4, cubic_b4, cubic_c4, cubic_d4;
float cubic_x[3];
float cubic_X, cubic_Y;
int cubic_solutions;
int cubic_checksum;

/*
  Initialization function
*/

__attribute__((always_inline)) static inline void
cubic_init(void) {
    cubic_a1 = 1.0f, cubic_b1 = -10.5f, cubic_c1 = 32.0f, cubic_d1 = -30.0f;
    cubic_a2 = 1.0f, cubic_b2 = -4.5f, cubic_c2 = 17.0f, cubic_d2 = -30.0f;
    cubic_a3 = 1.0f, cubic_b3 = -3.5f, cubic_c3 = 22.0f, cubic_d3 = -31.0f;
    cubic_a4 = 1.0f, cubic_b4 = -13.7f, cubic_c4 = 1.0f, cubic_d4 = -35.0f;
    cubic_X = 0, cubic_Y = 0;
    cubic_checksum = 0;
}

/*
  Return function
*/

__attribute__((always_inline)) static inline int
cubic_return(void) {
    if (cubic_checksum == 1051)
        return 0;
    else
        return -1;
}

/*
  Main functions
*/

__attribute__((noinline)) __attribute__((export_name("entrypoint")))
__attribute__((noinline)) __attribute__((export_name("entrypoint"))) void
cubic_main(void) {
    /* solve some cubic functions */
    /* should get 3 solutions: 2, 6 & 2.5   */
    cubic_solveCubic(cubic_a1, cubic_b1, cubic_c1, cubic_d1, &cubic_solutions,
                     cubic_x);
    cubic_checksum += cubic_solutions;
    cubic_solveCubic(cubic_a2, cubic_b2, cubic_c2, cubic_d2, &cubic_solutions,
                     cubic_x);
    cubic_checksum += cubic_solutions;
    cubic_solveCubic(cubic_a3, cubic_b3, cubic_c3, cubic_d3, &cubic_solutions,
                     cubic_x);
    cubic_checksum += cubic_solutions;
    cubic_solveCubic(cubic_a4, cubic_b4, cubic_c4, cubic_d4, &cubic_solutions,
                     cubic_x);
    cubic_checksum += cubic_solutions;

    /* Now solve some random equations */
    __pragma_loopbound(5, 5);
    for (cubic_a1 = 1; cubic_a1 < 10; cubic_a1 += 2) {
        __pragma_loopbound(5, 5);
        for (cubic_b1 = 10; cubic_b1 > 0; cubic_b1 -= 2) {
            __pragma_loopbound(7, 7);
            for (cubic_c1 = 5; cubic_c1 < 15; cubic_c1 += 1.5f) {
                __pragma_loopbound(5, 5);
                for (cubic_d1 = -1; cubic_d1 > -11; cubic_d1 -= 2) {
                    cubic_solveCubic(cubic_a1, cubic_b1, cubic_c1, cubic_d1,
                                     &cubic_solutions, cubic_x);
                    cubic_checksum += cubic_solutions;
                }
            }
        }
    }
}

__attribute__((always_inline)) static inline void
cubic_solveCubic(float a, float b, float c, float d, int *solutions, float *x) {
    float a1 = b / a, a2 = c / a, a3 = d / a;
    float Q = (a1 * a1 - 3.0f * a2) / 9.0f;
    float R = (2.0f * a1 * a1 * a1 - 9.0f * a1 * a2 + 27.0f * a3) / 54.0f;
    float R2_Q3 = R * R - Q * Q * Q;
    float theta;

    if (R2_Q3 <= 0) {
        *solutions = 3;
        theta =
            basicmath___ieee754_acosf(R / basicmath___ieee754_sqrtf(Q * Q * Q));
        x[0] = -2.0f * basicmath___ieee754_sqrtf(Q) *
                   basicmath___cosf(theta / 3.0f) -
               a1 / 3.0f;
        x[1] = -2.0f * basicmath___ieee754_sqrtf(Q) *
                   basicmath___cosf((theta + 2.0f * PI) / 3.0f) -
               a1 / 3.0f;
        x[2] = -2.0f * basicmath___ieee754_sqrtf(Q) *
                   basicmath___cosf((theta + 4.0f * PI) / 3.0f) -
               a1 / 3.0f;
    } else {
        *solutions = 1;
        x[0] = basicmath___ieee754_powf(
            basicmath___ieee754_sqrtf(R2_Q3) + basicmath___fabsf(R), 1 / 3.0f);
        x[0] += Q / x[0];
        x[0] *= (R < 0.0f) ? 1 : -1;
        x[0] -= a1 / 3.0f;
    }
}

__attribute__((noinline)) __attribute__((export_name("main")))
__attribute__((noinline)) __attribute__((export_name("main"))) int
main(void) {
    cubic_init();
    cubic_main();
    return cubic_return();
}
