Browse Source

Merge pull request #178 from hallzy/master

Added some functionality to Math class
Marco Bambini 8 years ago
parent
commit
ec143bb2ca

+ 1 - 0
.gitignore

@@ -283,6 +283,7 @@ paket-files/
 *.hex
 gravity
 unittest
+!test/unittest
 
 # Debug files
 *.dSYM/

+ 6 - 0
docs/types.html

@@ -91,6 +91,9 @@
 	var d = 0B0101;		// binary
 
 	var e = Int.random(1, 10) // Returns a random integer between 1 and 10 inclusive
+
+	var f = 30.radians // returns the result of converting 30 degrees to radians
+	var f = 3.degrees // returns the result of converting 3 radians to degrees
 			</code></pre>
 			
 			<h4 class="section-h4">Float</h4>
@@ -98,6 +101,9 @@
 			<pre><code class="swift">
 	var a = 3.1415;		// float
 	var b = 1.25e2;		// scientific notation
+
+	var f = 30.5.radians // returns the result of converting 30.5 degrees to radians
+	var f = 3.14.degrees // returns the result of converting 3.14 radians to degrees
 			</code></pre>
 			
 			<h4 class="section-h4">String</h4>

+ 140 - 1
src/optionals/gravity_math.c

@@ -34,6 +34,7 @@
 #define POW                         pow
 #define EXP                         exp
 #define SQRT                        sqrt
+#define CBRT                        cbrt
 #else
 #define SIN                         sinf
 #define COS                         cosf
@@ -49,6 +50,7 @@
 #define POW                         powf
 #define EXP                         expf
 #define SQRT                        sqrtf
+#define CBRT                        cbrtf
 #endif
 
 static gravity_class_t              *gravity_class_math = NULL;
@@ -190,6 +192,28 @@ static bool math_atan2 (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
 }
 
+static bool math_cbrt (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_value_t value = GET_VALUE(1);
+
+    if (VALUE_ISA_NULL(value)) {
+        RETURN_VALUE(VALUE_FROM_INT(0), rindex);
+    }
+
+    if (VALUE_ISA_INT(value)) {
+        gravity_float_t computed_value = (gravity_float_t)CBRT((gravity_float_t)value.n);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    if (VALUE_ISA_FLOAT(value)) {
+        gravity_float_t computed_value = (gravity_float_t)CBRT((gravity_float_t)value.f);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    // should be NaN
+    RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+}
+
 // returns x, rounded upwards to the nearest integer
 static bool math_ceil (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
@@ -282,6 +306,59 @@ static bool math_floor (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
 }
 
+static int gcf(int x, int y) {
+    if (x == 0) {
+        return y;
+    }
+    while (y != 0) {
+        if (x > y) {
+            x = x - y;
+        }
+        else {
+            y = y - x;
+        }
+    }
+    return x;
+}
+
+static bool math_gcf (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused (vm, rindex)
+    if (nargs < 3) RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+
+    for (int i = 1; i < nargs; ++i) {
+        if (!VALUE_ISA_INT(GET_VALUE(i))) RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+    }
+
+    int gcf_value = (int)GET_VALUE(1).n;
+
+    for (int i = 1; i < nargs-1; ++i) {
+        gcf_value = gcf(gcf_value, (int)GET_VALUE(i+1).n);
+    }
+
+    RETURN_VALUE(VALUE_FROM_INT(gcf_value), rindex);
+}
+
+static int lcm(int x, int y) {
+    return x*y/gcf(x,y);
+}
+
+static bool math_lcm (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused (vm, rindex)
+    if (nargs < 3) RETURN_ERROR("2 or more arguments expected");
+
+    for (int i = 1; i < nargs; ++i) {
+        if (!VALUE_ISA_INT(GET_VALUE(i))) RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+    }
+
+    int lcm_value = (int)GET_VALUE(1).n;
+
+    for (int i = 1; i < nargs-1; ++i) {
+        lcm_value = lcm(lcm_value, (int)GET_VALUE(i+1).n);
+    }
+
+    RETURN_VALUE(VALUE_FROM_INT(lcm_value), rindex);
+}
+
 // returns the natural logarithm (base E) of x
 static bool math_log (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
@@ -305,6 +382,63 @@ static bool math_log (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
     RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
 }
 
+// returns the base 10 logarithm of x
+static bool math_log10 (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_value_t value = GET_VALUE(1);
+
+    if (VALUE_ISA_NULL(value)) {
+        RETURN_VALUE(VALUE_FROM_INT(0), rindex);
+    }
+
+    if (VALUE_ISA_INT(value)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.n)/(gravity_float_t)LOG((gravity_float_t)10);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    if (VALUE_ISA_FLOAT(value)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.f)/(gravity_float_t)LOG((gravity_float_t)10);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    // should be NaN
+    RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+}
+
+// returns the logarithm (base x) of y
+static bool math_logx (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_value_t base = GET_VALUE(1);
+    gravity_value_t value = GET_VALUE(2);
+
+    if (VALUE_ISA_NULL(value)) {
+        RETURN_VALUE(VALUE_FROM_INT(0), rindex);
+    }
+
+    if (VALUE_ISA_INT(value) && VALUE_ISA_INT(base)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.n)/(gravity_float_t)LOG((gravity_float_t)base.n);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    if (VALUE_ISA_INT(value) && VALUE_ISA_FLOAT(base)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.n)/(gravity_float_t)LOG((gravity_float_t)base.f);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    if (VALUE_ISA_FLOAT(value) && VALUE_ISA_INT(base)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.f)/(gravity_float_t)LOG((gravity_float_t)base.n);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    if (VALUE_ISA_FLOAT(value) && VALUE_ISA_FLOAT(base)) {
+        gravity_float_t computed_value = (gravity_float_t)LOG((gravity_float_t)value.f)/(gravity_float_t)LOG((gravity_float_t)base.f);
+        RETURN_VALUE(VALUE_FROM_FLOAT(computed_value), rindex);
+    }
+
+    // should be NaN
+    RETURN_VALUE(VALUE_FROM_UNDEFINED, rindex);
+}
+
 // returns the number with the highest value
 static bool math_max (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     gravity_float_t n = FLOAT_MIN;
@@ -383,7 +517,7 @@ static bool math_random (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     }
     
     int r = rand();
-    RETURN_VALUE(VALUE_FROM_FLOAT(r / RAND_MAX), rindex);
+    RETURN_VALUE(VALUE_FROM_FLOAT((float)r / RAND_MAX), rindex);
 }
 
 // rounds x to the nearest integer
@@ -538,11 +672,16 @@ static void create_optional_class (void) {
     gravity_class_bind(meta, "asin", NEW_CLOSURE_VALUE(math_asin));
     gravity_class_bind(meta, "atan", NEW_CLOSURE_VALUE(math_atan));
     gravity_class_bind(meta, "atan2", NEW_CLOSURE_VALUE(math_atan2));
+    gravity_class_bind(meta, "cbrt", NEW_CLOSURE_VALUE(math_cbrt));
     gravity_class_bind(meta, "ceil", NEW_CLOSURE_VALUE(math_ceil));
     gravity_class_bind(meta, "cos", NEW_CLOSURE_VALUE(math_cos));
     gravity_class_bind(meta, "exp", NEW_CLOSURE_VALUE(math_exp));
     gravity_class_bind(meta, "floor", NEW_CLOSURE_VALUE(math_floor));
+    gravity_class_bind(meta, "gcf", NEW_CLOSURE_VALUE(math_gcf));
+    gravity_class_bind(meta, "lcm", NEW_CLOSURE_VALUE(math_lcm));
     gravity_class_bind(meta, "log", NEW_CLOSURE_VALUE(math_log));
+    gravity_class_bind(meta, "log10", NEW_CLOSURE_VALUE(math_log10));
+    gravity_class_bind(meta, "logx", NEW_CLOSURE_VALUE(math_logx));
     gravity_class_bind(meta, "max", NEW_CLOSURE_VALUE(math_max));
     gravity_class_bind(meta, "min", NEW_CLOSURE_VALUE(math_min));
     gravity_class_bind(meta, "pow", NEW_CLOSURE_VALUE(math_pow));

+ 36 - 0
src/runtime/gravity_core.c

@@ -1415,6 +1415,18 @@ static bool float_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     RETURN_VALUE(v, rindex);
 }
 
+static bool float_degrees (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	// Convert the float from radians to degrees
+	RETURN_VALUE(VALUE_FROM_FLOAT(GET_VALUE(0).f*180/3.141592653589793), rindex);
+}
+
+static bool float_radians (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	// Convert the float from degrees to radians
+	RETURN_VALUE(VALUE_FROM_FLOAT(GET_VALUE(0).f*3.141592653589793/180), rindex);
+}
+
 // MARK: - Int Class -
 
 // binary operators
@@ -1550,6 +1562,18 @@ static bool int_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
     RETURN_VALUE(v, rindex);
 }
 
+static bool int_degrees (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	// Convert the int from radians to degrees
+	RETURN_VALUE(VALUE_FROM_FLOAT(GET_VALUE(0).n*180/3.141592653589793), rindex);
+}
+
+static bool int_radians (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	// Convert the int from degrees to radians
+	RETURN_VALUE(VALUE_FROM_FLOAT(GET_VALUE(0).n*3.141592653589793/180), rindex);
+}
+
 // MARK: - Bool Class -
 
 static bool operator_bool_add (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -2452,6 +2476,10 @@ static void gravity_core_init (void) {
 	gravity_class_bind(gravity_class_int, GRAVITY_OPERATOR_NEG_NAME, NEW_CLOSURE_VALUE(operator_int_neg));
 	gravity_class_bind(gravity_class_int, GRAVITY_OPERATOR_NOT_NAME, NEW_CLOSURE_VALUE(operator_int_not));
 	gravity_class_bind(gravity_class_int, GRAVITY_INTERNAL_LOOP_NAME, NEW_CLOSURE_VALUE(int_loop));
+    closure = computed_property_create(NULL, NEW_FUNCTION(int_radians), NULL);
+	gravity_class_bind(gravity_class_int, "radians", VALUE_FROM_OBJECT(closure));
+    closure = computed_property_create(NULL, NEW_FUNCTION(int_degrees), NULL);
+	gravity_class_bind(gravity_class_int, "degrees", VALUE_FROM_OBJECT(closure));
 	// Meta
 	gravity_class_t *int_meta = gravity_class_get_meta(gravity_class_int);
 	gravity_class_bind(int_meta, "random", NEW_CLOSURE_VALUE(int_random));
@@ -2471,6 +2499,10 @@ static void gravity_core_init (void) {
 	gravity_class_bind(gravity_class_float, "round", NEW_CLOSURE_VALUE(function_float_round));
 	gravity_class_bind(gravity_class_float, "floor", NEW_CLOSURE_VALUE(function_float_floor));
 	gravity_class_bind(gravity_class_float, "ceil", NEW_CLOSURE_VALUE(function_float_ceil));
+    closure = computed_property_create(NULL, NEW_FUNCTION(float_radians), NULL);
+	gravity_class_bind(gravity_class_float, "radians", VALUE_FROM_OBJECT(closure));
+    closure = computed_property_create(NULL, NEW_FUNCTION(float_degrees), NULL);
+	gravity_class_bind(gravity_class_float, "degrees", VALUE_FROM_OBJECT(closure));
     // Meta
     gravity_class_t *float_meta = gravity_class_get_meta(gravity_class_float);
     gravity_class_bind(float_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(float_exec));
@@ -2597,6 +2629,10 @@ void gravity_core_free (void) {
     computed_property_free(gravity_class_map, "count", true);
     computed_property_free(gravity_class_range, "count", true);
     computed_property_free(gravity_class_string, "length", true);
+    computed_property_free(gravity_class_int, "radians", true);
+    computed_property_free(gravity_class_int, "degrees", true);
+    computed_property_free(gravity_class_float, "radians", true);
+    computed_property_free(gravity_class_float, "degrees", true);
     gravity_class_t *system_meta = gravity_class_get_meta(gravity_class_system);
     computed_property_free(system_meta, "gcenabled", true);
         

+ 13 - 0
test/unittest/math/cbrt.gravity

@@ -0,0 +1,13 @@
+#unittest {
+	name: "Test Math.cbrt()";
+	error: NONE;
+	result: true;
+};
+
+func main() {
+	var r1 = Math.cbrt(8) == 2
+	var r2 = Math.cbrt(125) == 5
+	var r3 = Math.cbrt(1000) == 10
+	var r4 = Math.cbrt(3.375) <= 1.500001 and Math.cbrt(3.375) >= 1.499999
+	return r1 and r2 and r3 and r4
+}

+ 12 - 0
test/unittest/math/gcf.gravity

@@ -0,0 +1,12 @@
+#unittest {
+	name: "Test Math.gcf()";
+	error: NONE;
+	result: true;
+};
+
+func main() {
+	var r1 = Math.gcf(10, 1) == 1
+	var r2 = Math.gcf(10, 10) == 10
+	var r3 = Math.gcf(12, 15, 21) == 3
+	return r1 and r2 and r3
+}

+ 12 - 0
test/unittest/math/lcm.gravity

@@ -0,0 +1,12 @@
+#unittest {
+	name: "Test Math.lcm()";
+	error: NONE;
+	result: true;
+};
+
+func main() {
+	var r1 = Math.lcm(10, 1) == 10
+	var r2 = Math.lcm(3, 4) == 12
+	var r3 = Math.lcm(6, 15, 2) == 30
+	return r1 and r2 and r3
+}

+ 20 - 0
test/unittest/math/log.gravity

@@ -0,0 +1,20 @@
+#unittest {
+	name: "Test Math.log(), Math.log10(), Math.logx()";
+	error: NONE;
+	result: true;
+};
+
+func main() {
+	var r1 = Math.log(1) == 0
+	var r2 = Math.log(Math.E) == 1
+	var r3 = Math.log(Math.E*Math.E) == 2
+
+	var r4 = Math.log10(1) == 0
+	var r5 = Math.log10(10) == 1
+	var r6 = Math.log10(100) == 2
+
+	var r7 = Math.logx(2, 2) == 1
+	var r8 = Math.logx(2, 4) == 2
+	var r9 = Math.logx(5, 25) == 2
+	return r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8 and r9
+}

+ 26 - 0
test/unittest/radian_degree_conversion.gravity

@@ -0,0 +1,26 @@
+#unittest {
+	name: "Test Int.radians, Int.degrees";
+	error: NONE;
+	result: true;
+};
+
+func main() {
+	// 360 degrees in radians is 2*pi
+	var ir1 = 360.radians == 2*Math.PI
+	// 180 degrees in radians is pi
+	var ir2 = 180.radians == Math.PI
+	// 30 degrees in radians is pi/6
+	var ir3 = 30.radians == Math.PI/6
+	var ir = ir1 and ir2 and ir3
+
+	// Test Int.degrees
+	// pi/3 radians in degrees is 60
+	var id1 = (Math.PI/3).degrees <= 60.000001 and (Math.PI/3).degrees >= 59.999999
+	// pi/4 radians in degrees is 45
+	var id2 = (Math.PI/4).degrees <= 45.000001 and (Math.PI/4).degrees >= 44.999999
+	// pi/2 radians in degrees is 90
+	var id3 = (Math.PI/2).degrees <= 90.000001 and (Math.PI/2).degrees >= 89.999999
+	var id = id1 and id2 and id3
+
+	return id and ir
+}