Download or view symbolicTrig.frink in plain text format or (experimental) run in browser
/** 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]]
Download or view symbolicTrig.frink in plain text format or (experimental) run in browser
This is a program written in the programming language Frink.
For more information, view the Frink
Documentation or see More Sample Frink Programs.
Alan Eliasen, eliasen@mindspring.com