/** This is an experiment to implement exact trigonometric values in Frink. It is a bit artificial in that it introduces new symbols PI and DEG. These may not be necessary if you only perform symbolic transforms, but it is defensive in the case you might eval[] something and want to preserve symbolic values until as late as possible to some specified arbitrary precision. To perform these calculations to arbitrary precision, you might do something like: See: https://en.wikipedia.org/wiki/Exact_trigonometric_values */ // Artificially introduce new units of measure, the "gangle" of which the // base unit is called "PI" (note uppercase). Defining this is *not* // necessary to make anything below work, but might be useful in another // tactic. gangle =!= PI // We don't do this here but rather handle the symbol DEG in the transformation // rules below. But we could do it here if it's useful somewhere // DEG := PI / 180 // Define transformation rules that can be applied to our new number system. // These are applied to an expression using transformExpression[expr] // // These are just a starting point. Can you add more rules? transformations symbolicTrig { DEG <-> PI/180 // Translate DEG in terms of PI sin[0] <-> 0 sin[PI] <-> 0 // Integer multiples of pi are all zero sin[_n is isInteger PI] <-> 0 // Exact values sin[_n is isRational PI] :: _n mod 2 == 1/2 <-> 1 // 90 degrees sin[_n is isRational PI] :: _n mod 2 == 3/2 <-> -1 // 270 degrees // These rules handle sin[2 PI n + angle] for rational angles and integer n. // Because of Frink's auto-simplification rules, a rational angle is usually // turned from something like sin[2 PI + 180 DEG] // into sin[3/2 PI] // where the 3/2 is a single exact rational number, which is what is matched // as the pattern _n below. // The first example matches sin[2 pi n + 1/12] (15 degrees) sin[_n is isRational PI] :: _n mod 2 == 1/12 <-> sqrt[2] (sqrt[3] - 1) / 4 // 15 degrees sin[_n is isRational PI] :: _n mod 2 == 1/10 <-> (sqrt[5] - 1) / 4 // 18 degrees sin[_n is isRational PI] :: _n mod 2 == 1/8 <-> sqrt[2 - sqrt[2]]/2 // 22.5 degrees sin[_n is isRational PI] :: _n mod 2 == 1/6 <-> 1/2 // 30 degrees sin[_n is isRational PI] :: _n mod 2 == 1/5 <-> sqrt[2] sqrt[5 - sqrt[5]] / 4 // 36 degrees sin[_n is isRational PI] :: _n mod 2 == 1/4 <-> 1/sqrt[2] // 45 degrees // Handle rational angles between 45 and 90 degrees sin[_n is isRational PI] :: (_n mod 2) > 1/4 and (_n mod 2) < 1/2 <-> sqrt[1 - sin[PI (1/2 - (_n mod 2))]^2] // Handle rational angles between 90 and 180 degrees sin[_n is isRational PI] :: (_n mod 2) > 1/2 and (_n mod 2) < 1 <-> sin[(1 - (_n mod 2)) PI] // Handle rational angles greater than 180 degrees // (transform to -sin[PI (_n - 1)]) sin[_n is isRational PI] :: _n mod 2 > 1 <-> -sin[PI ((_n mod 2) - 1)] // The tactic to extend these from sin to cos is to transform // cos[_x] to sin[_x + PI/2] which means we have to write half as many // relations. cos[_x] <-> sin[_x + PI/2] // And, wow, now that we have sin and cos, we can easily define tan // (which is probably not fully simplified) tan[_x] <-> sin[_x] / cos[_x] } // Include libraries to perform arbitrary-precision calculations. use pi2.frink use e.frink use toArbitrary.frink use ArbitraryPrecision.frink // Evaluate the expression x by first converting its arguments to // arbitrary-precision equivalents and evaluating to the specified number of // digits. // This is becoming incredibly powerful and leading Frink into automatic // arbitrary precision. N[x, digits=getPrecision[]] := { origPrec = getPrecision[] x2 = substituteExpression[x, noEval[PI], noEval[pi]] x2 = toArbitrary[x2] try { setPrecision[digits] println["Expression was: " + inputForm[x]] println["After toArbitrary: " + inputForm[x2]] y = unsafeEval[x2, true] // Show exception if there is one // println["y is $y"] return y } finally setPrecision[origPrec] } // Test code symbolicMode[true] showApproximations[false] println[] x = parseToExpression["sin[0]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[90 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[180 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI]"] println[x] println[transformExpression[x]] println[] x = noEval[sin[2 PI + 30 DEG]] println[x] println[transformExpression[x]] println[] x = noEval[sin[2 PI + 60 DEG]] println[x] println[transformExpression[x]] println[] x = noEval[sin[2 PI + 120 DEG]] println[x] println[transformExpression[x]] println[] x = noEval[sin[2 PI + 240 DEG]] println[x] println[transformExpression[x]] println[] x = noEval[cos[2 PI + 30 DEG]] println[x] println[transformExpression[x]] println[] x = noEval[sin[2 PI + 54 DEG]] println[x] println[transformExpression[x]] // Equivalent to above but in terms of pi radians, not degrees println[] x = noEval[cos[2 PI + PI/6]] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 45 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 135 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + (45 + 180) DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + (45 + 180 + 90) DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[PI/2]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[3 PI/2]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[-3 PI/2]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[15 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 75 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[105 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 105 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 180 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 270 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 195 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["sin[2 PI + 285 DEG]"] println[x] println[formatTable[transformExpressionDebug[x]]] println[] x = parseToExpression["sin[2 PI + 300 DEG]"] println[x] println[formatTable[transformExpressionDebug[x]]] // This can't be solved by the above rules but maybe you can add one? println[] x = parseToExpression["cos[15 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["tan[30 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["cos[0 DEG]"] println[x] println[transformExpression[x]] println[] x = parseToExpression["cos[90 DEG]"] println[x] println[transformExpression[x]] // If you want to numerically evaluate results that are not exactly evaluated // by any of the above, you can do something like the following that substitutes // PI with pi println[] x = parseToExpression["sin[1 DEG]"] println[x] t = transformExpression[x] //println["t is $t"] //println["about to call N[t]"] y = N[t, 60] //println[y] println["Evaluated: " + eval[y]]