cos, sin, tan?

Can’t find any language for this.

Use the Mathematical Functions which is part of Satimage.osax

If you have Excel, you could use that function or you could use the series expansion to calculate it.

on Sin(x)
	set maxLoop to 19
	set factorial to 1
	set retVal to 0
	repeat with i from 1 to maxLoop by 2
		set term to (-1 ^ ((i mod 4 - 1) / 2)) * (x ^ i) / factorial
		set retVal to retVal + term
		set factorial to factorial * (i + 1) * (i + 2)
	end repeat
	return retVal
end Sin

on xlSin(x)
	tell application "Microsoft Excel"
		return (execute excel 4 macro VB macro name "SIN(" & x & ")")
	end tell
end xlSin

Cos x = 1 - x^2/2 + x^4/4! - x^6/6! + …

JavaScript is supposed to be very good at this kind of Math (Im not BTW much has been forgotten since school) so you could always have Safari help you out doing the math by doing its JavaScript and passing you back the result (I only say this as you should have this app installed there are other apps that can do JavaScript too so check) This should give you some idea anyhow.

set JS to "function getAngle() {
// Should have right angle triangle
var adjSide = 10;
var oppSide = 16;
var myAngle = (Math.atan(oppSide / adjSide)) * (180 / Math.PI);
return myAngle;
};
getAngle();"

tell application "Safari"
	activate
	set MyMath to do JavaScript JS in front document
	display dialog the result
end tell

Hi,

…or simply using awk’s arithmetic built-in functions

-- - available functions
-- - cos(x); exp(ex); int(x); log(x) ; sin(x); sqrt(x); atan2(y,x); rand(); srand(x) 

set fct to "cos"
set angle to 30 -- in degree

set x to (do shell script "awk 'BEGIN{ pi = 4.0*atan2(1.0,1.0); degree = pi/180.0; print " & fct & "(" & angle & "*degree)}'") as real

if you need precision use the printf statement:

set fct to "cos"
set angle to 30 -- in degree
set precision to 12 -- interger

set x to (do shell script "awk 'BEGIN{ pi = 4.0*atan2(1.0,1.0); degree = pi/180.0; calc=" & fct & "(" & angle & "*degree); printf(\"%." & precision & "f\\n\", calc)}'") as real

and to calculate the tangent:

set angle to 30 -- in degree
set precision to 12 -- interger

set |tan| to do shell script "awk 'BEGIN{
pi = 4.0*atan2(1.0,1.0)
degree = pi/180.0
sinus = sin(" & angle & " * degree)
cosinus = cos(" & angle & " * degree)
print sinus/cosinus
}'"

you could also use the bc’s math library:

-- s (x) -- sine of x, x  in radians.
-- c (x) -- cosine of x, x  in radians.
-- a (x) -- arctangent of x, arctangent returns radians.
-- l (x) -- natural logarithm of x.
-- e (x) -- exponential function of raising e to the value x.
-- j (n,x) -- bessel function of integer order n of x.

set |angle°| to 60
set fct to "c"
set precision to 13 -- interger

set y to do shell script "echo \"scale=" & precision & "; " & fct & "(" & |angle°| & " * (4 * a(1) / 180))\" | bc -l"

hth

Or simply (a bit more concise version of Mark67’s answer):


on math(f, x)
	return run script "Math." & f & "(" & x & ")" in "JavaScript"
end math

on sin(x)
	math("sin", x)
end sin

on cos(x)
	math("cos", x)
end cos

on random()
	math("random", "")
end random

log "sin(pi as real / 6): " & sin((pi as real) / 6) (*sin(pi as real / 6): 0.5*)
log "cos(pi as real / 6): " & cos((pi as real) / 6) (*cos(pi as real / 6): 0.866025403785*)
log "random(): " & random()  (*random(): 0.687339054003*)

And similarly for abs, acos, acosh, asin, asinh, atan, atanh, cbrt, ceil, clz32, cosh, exp, expm1, floor, fround, log, log1p, log10, log2, round, sign, sin, sinh, sqrt, tan, tanh, and trunc.

As atan2, imul, and pow need 2 parameters, we could have some math2(f, x, y) method.

As hypot, max, and min can have many parameters in JavaScript, we need a mathN(f, list_), too, and listToString() in order to concatenate the list using “,”:


on listToString(list_)
	set AppleScript's text item delimiters to ", "
	set string_ to list_ as text
	set AppleScript's text item delimiters to {""} --> restore delimiters to default
	string_
end listToString

on mathN(f, list_)
	run script "Math." & f & "(" & parameters & ")" in "JavaScript"
end mathN

on max(list_)
	mathN("max", list_)
end max

on min(list_)
	mathN("min", list_)
end min

log "max({5,9,3,10,4}): " & max({5, 9, 3, 10, 4}) (*max({5,9,3,10,4}): 10*)
log "min({5,9,3,10,4}): " & min({5, 9, 3, 10, 4}) (*min({5,9,3,10,4}): 3*)

(Note that mathN could be used for the methods with just one parameter and of course also for those that need two, e.g., mathN(“sin”, {x}) and mathN(“pow”, {x, y}…)

There are some other cool possibilities of integrating JavaScript within AppleScript:

  1. Sorting a list is certainly quite useful and while for instance the simple_sort at https://www.macosxautomation.com/applescript/sbrt/sbrt-05.html has ~20 lines in pure AppleScript, with JavaScript it can be done on a single line in pretty straightforward way:

on sort(list_)
	run script "[" & listToString(list_) & "].sort((n1, n2) => n1 - n2)" in "JavaScript"
end sort

log "sort({5, 9, 3, 10, 4}): " & listToString(sort({5, 9, 3, 10, 4})) (*sort({5, 9, 3, 10, 4}): 3, 4, 5, 9, 10*)

  1. Let’s now be a bit more adventurous and generate an array of randomly shuffled numbers:

on shuffle(n)
	run script "[...Array(" & n & ").keys()].map((i)=>({at:Math.random(),value:i+1})).sort((x,y)=>x.at-y.at).map((x)=>x.value)" in "JavaScript"
end shuffle

log "shuffle(10): " & listToString(shuffle(10)) (*shuffle(10): 9, 6, 4, 1, 2, 5, 7, 3, 10, 8*)

Another ±1-liner! But the main point is that we can use just a single expression in the new JavaScript ES6 that now supports => arrow functions, the ‘…’ spread operator and plenty of good array functions - such as keys(), map(), and again sort() - which can greatly simplify our AppleScreepy lifes. And thankfully JavaScript ES6 is now supported by AppleScript!

PS 1. Isn’t it kinda funny that we use sorting to shuffle a list? (The algorithm is cool, it first creates an array without values, then fills it with the indices using ‘…’ and key(). Using the first map() it then fills the array with objects with a random position and a value derived from each index. These objects can then be sorted according to their random position and then the second map() just extracts the values.)

PS 2. I have to admit that in order to put together shuffle() I had to steal the main components from two StackOverflow wizzards: Ben in Does JavaScript have a method like “range()” to generate a range within the supplied bounds? taught me to use ‘…’ and key() and superluminary in How to randomize (shuffle) a JavaScript array? baffled me with the really ingenious way how to ‘randomly sort’ an array. (Note that this implementation is a bit slow not only because all the map() and key() calls traverse the entire array but more importantly because sort() can be the big O(n^2) deal while other shuffle algorithms need just O(n) - so use only for small arrays) :confused:

Caveat:
Another interesting possibility is to use JavaScript’s String functions that allow for RegExp (regular expressions) to simplify coding in AppleScript:


# Returns 'duality' surrounded by single quotes if it's a string, or untouched if it's a RegExp
on quotedIfString(duality)
	if first character of duality is "/" then return duality -- a RegExp, just return it
	return quoted form of duality -- it's a string, return it in quotes
end quotedIfString

on match(inString, regExp)
	run script "'" & inString & "'.match(" & regExp & ")" in "JavaScript"
end match

on replace(inString, duality, byString)
	run script "'" & inString & "'.replace(" & quotedIfString(duality) & ",'" & byString & "')" in "JavaScript"
end replace

on split(text_, duality)
	run script "'" & text_ & "'.split(" & quotedIfString(duality) & ")" in "JavaScript"
end split

on niceListString(list_)
	"[{" & replace(listToString(list_), "/, /g", "}, {") & "}]"
end niceListString

set niceList to niceListString(match("/Users/jan/.atom/styles.less", "/(.*\\/)(.+?)\\.(\\w+)$/"))

log "[{all, folder, name, extension}]: " & niceList
	(*{all, folder, name, extension}: [{/Users/jan/.atom/styles.less}, {/Users/jan/.atom/}, {styles}, {less}]*)
log "extension: " & item 5 of split(niceList, "/[\\[{, }\\]]{2,4}/") (*extension: less*)
log "extension: " & item 4 of split(replace(niceList, "/\\[{|}\\]/g", ""), "}, {") (*extension: less*)
log "extension: " & item 4 of (run script "'" & niceList & "'.replace('[{', '').replace('}]', '').split('}, {')" in "JavaScript") (*extension: less*)

Note 1: '' in a RegExp must be doubled as ‘\’.
Note 2: Obviously an ‘extension’ could be easily obtained as item 4 in the match(). But the contrived ways using the complicated niceList demonstrate nicely how useful are JavaScript’s split() and replace() with and/or without a RegExp

…And of course how useful it can be to integrate JavaScript’s features in AppleScript.

PS 3. Maybe this post could/should go to a separate category given it’s generic and not just about Math?

Here are some AppleScript routines to calculate Sine, Cosine, & Tangent
These are from a library I downloaded years ago. Not sure where.

on sine(x)
	local answer, numerator, denominator, factor
	if x ≥ 0 then
		set x to x mod 360
	else
		set x to 360 + (x mod 360)
	end if
	display alert x
	--convert from degrees to radians
	set x to x * (2 * pi) / 360
	
	set answer to 0
	set numerator to x
	set denominator to 1
	set factor to -(x ^ 2)
	
	repeat with i from 3 to 40 by 2
		set answer to answer + numerator / denominator
		set numerator to numerator * factor
		set denominator to denominator * i * (i - 1)
	end repeat
	
	return answer
end sine

on cosine(x)
	local answer, numerator, denominator, factor
	if x ≥ 0 then
		set x to x mod 360
	else
		set x to 360 + (x mod 360)
	end if
	
	--convert from degrees to radians
	set x to x * (2 * pi) / 360
	
	set answer to 0
	set numerator to 1
	set denominator to 1
	set factor to -(x ^ 2)
	
	repeat with i from 2 to 40 by 2
		set answer to answer + numerator / denominator
		set numerator to numerator * factor
		set denominator to denominator * i * (i - 1)
	end repeat
	
	return answer
end cosine

--x is in degrees
on tan(x)
	local answer
	set answer to sine(x) / (cosine(x))
	return answer
end tan

on tangent(x)
	local factor, sanswer, canswer, snum, cnum, sdenom, cdenom
	if x ≥ 0 then
		set x to x mod 360
	else
		set x to 360 + (x mod 360)
	end if
	
	--convert from degrees to radians
	set x to x * (2 * pi) / 360
	
	set {sanswer, canswer} to {0, 0}
	set {cnum, snum} to {1, x}
	set {cdenom, sdenom} to {1, 1}
	set factor to -(x ^ 2)
	
	repeat with i from 2 to 40 by 2
		set canswer to canswer + cnum / cdenom
		set sanswer to sanswer + snum / sdenom --(cdenom * (i + 1))
		set cnum to cnum * factor
		set snum to snum * factor
		set cdenom to cdenom * i * (i - 1)
		set sdenom to cdenom * (i + 1)
	end repeat
	
	return sanswer / canswer
end tangent

You’ll notice there are 2 versions on Tangent