1. Proposal for 'math.e' (2007-08-07)
This is my current (2007-08-07) proposal for a "math.e" standard
include file, according to the recent discussion here on EUforum.
Changes compared to the previous version:
- Now the global constants have maximum available precision ( hopefully
).
Note:
E.g. EULER_NORMAL = 1/sqrt(TWO_PI) has the same precision as
EULER_NORMAL = machine_func(47, {81,54,212,51,69,136,217,63})
- Aded two new global constants.
- Added internal constants RADIANS_TO_DEGREES and DEGREES_TO_RADIANS.
- Added a general logarithm function.
- Added functions polar_to_rect() and rect_to_polar().
- Renamed 4 functions, according to this post by Fernando Bauer
<http://www.openeuphoria.org/cgi-bin/esearch.exu?fromMonth=8&fromYear=C&toMonth=8&toYear=C&postedBy=Fernando+Bauer&keywords=Aug+3>
and to the discussion which it triggered.
- find_least() -> find_min()
- find_greatest() -> find_max()
- least() -> min()
- greatest() -> max()
global constant
E = 2.7182818284590452,
PI = 3.1415926535897932,
EULER_GAMMA = 0.57721566490153286, -- the Euler-Mascheroni-Soldner gamma
constant
LN2 = log(2),
LN10 = log(10),
SQRT2 = sqrt(2),
HALF_SQRT2 = SQRT2/2.0,
HALF_PI = PI/2.0,
QUARTER_PI = PI/4.0,
TWO_PI = PI*2.0,
EULER_NORMAL = 1/sqrt(TWO_PI)
global function log2 (object x)
-- logarithm base 2
-- in : (sequence of) real number(s) > 0
-- out: (sequence of) real number(s)
-- Note: This function returns _exact_ results for all integral
-- powers of 2 in the half-closed interval ]0,#FFFFFFFF]
if atom(x) then
if x = #20000000 then
return 29 -- log(x)/LN2 is imprecise in this case
elsif x = #80000000 then
return 31 -- log(x)/LN2 is imprecise in this case
else
return log(x)/LN2
end if
end if
for i = 1 to length(x) do
x[i] = log2(x[i])
end for
return x
end function
global function log10 (object x)
-- logarithm base 10
-- in : (sequence of) real number(s) > 0
-- out: (sequence of) real number(s)
return log(x)/LN10
end function
type positive_not_1 (object x)
if atom(x) and x > 0 then
return x != 1
end if
return 0
end type
global function logx (object x, positive_not_1 base)
-- general logarithm function
-- in : x : (sequence of) atom(s) > 0
-- base: atom > 0 and != 1
-- out : (sequence of) atom(s)
-- Note: If x = 1 then the function returns 0 for any possible base.
return log(x)/log(base)
end function
global function exp (object x)
return power(E, x)
end function
global function sinh (object x)
return (exp(x) - exp(-x)) / 2
end function
global function cosh (object x)
return (exp(x) + exp(-x)) / 2
end function
global function tanh (object x)
return sinh(x) / cosh(x)
end function
global function arcsinh (object x)
return log(x + sqrt(x*x+1))
end function
type not_below_1 (object x)
if atom(x) then
return x >= 1.0
end if
for i = 1 to length(x) do
if not not_below_1(x[i]) then
return 0
end if
end for
return 1
end type
global function arccosh (not_below_1 x)
return log(x + sqrt(x*x-1))
end function
type abs_below_1 (object x)
if atom(x) then
return x > -1.0 and x < 1.0
end if
for i = 1 to length(x) do
if not abs_below_1(x[i]) then
return 0
end if
end for
return 1
end type
global function arctanh (abs_below_1 x)
return log((1+x)/(1-x)) / 2
end function
global function abs (object x)
-- return the absolute value of (all elements of) x
if atom(x) then
if x < 0 then
return -x
else
return x
end if
end if
for i = 1 to length(x) do
x[i] = abs(x[i])
end for
return x
end function
global function sign (object x)
-- x < 0 ==> -1
-- x = 0 ==> 0
-- x > 0 ==> +1
if atom(x) then
return compare(x, 0)
end if
for i = 1 to length(x) do
x[i] = sign(x[i])
end for
return x
end function
global function ceil (object x)
-- the opposite of floor()
-- Examples: ? ceil(3.2) --> 4
-- ? ceil({-3.2,7,1.6}) --> {-3,7,2}
return -floor(-x)
end function
global function sum (sequence list)
-- Return the sum of all elements in 'list'.
-- Note: This does not do a recursive sum of sub-sequences.
atom ret
ret = 0
for i = 1 to length(list) do
ret += list[i]
end for
return ret
end function
constant RADIANS_TO_DEGREES = 180.0/PI
global function radians_to_degrees (object x)
-- in : (sequence of) angle(s) in radians
-- out: (sequence of) angle(s) in degrees
return x * RADIANS_TO_DEGREES
end function
constant DEGREES_TO_RADIANS = PI/180.0
global function degrees_to_radians (object x)
-- in : (sequence of) angle(s) in degrees
-- out: (sequence of) angle(s) in radians
return x * DEGREES_TO_RADIANS
end function
type trig_range (object x)
-- values passed to arccos and arcsin must be [-1,+1]
if atom(x) then
return x >= -1 and x <= 1
end if
for i = 1 to length(x) do
if not trig_range(x[i]) then
return 0
end if
end for
return 1
end type
global function arcsin (trig_range x)
-- returns angle in radians
return 2 * arctan(x / (1.0 + sqrt(1.0 - x * x)))
end function
global function arccos (trig_range x)
-- returns angle in radians
return HALF_PI - 2 * arctan(x / (1.0 + sqrt(1.0 - x * x)))
end function
type point_pol (object x)
if sequence(x) and (length(x) = 2)
and atom(x[1]) and (x[1] >= 0)
and atom(x[2]) then
return 1
end if
return 0
end type
global function polar_to_rect (point_pol p)
-- convert polar coordinates to rectangular coordinates
-- in : sequence of two atoms: {distance, angle};
-- 'distance' must be >= 0, 'angle' is in radians
-- out: sequence of two atoms: {x, y}
atom distance, angle, x, y
distance = p[1]
angle = p[2]
x = distance*cos(angle)
y = distance*sin(angle)
return {x, y}
end function
type point_xy (object x)
if sequence(x) and (length(x) = 2)
and atom(x[1])
and atom(x[2]) then
return 1
end if
return 0
end type
global function rect_to_polar (point_xy p)
-- convert rectangular coordinates to polar coordinates
-- in : sequence of two atoms: {x, y}
-- out: sequence of two elements: {distance, angle}
-- - 'distance' is always an atom >= 0
-- - 'angle' is normally an atom that expresses radians,
-- and is in the half-closed interval ]-PI,+PI].
-- If 'distance' equals 0, then 'angle' is {},
-- meaning that it is undefined in this case.
object angle
atom distance, x, y
x = p[1]
y = p[2]
distance = sqrt(x*x + y*y)
if x > 0 then
angle = arctan(y/x)
elsif x < 0 then
if y < 0 then
angle = arctan(y/x) - PI
else
angle = arctan(y/x) + PI
end if
else
if y < 0 then
angle = -HALF_PI
elsif y > 0 then
angle = HALF_PI
else
angle = {} -- The angle is undefined in this case.
end if
end if
return {distance, angle}
end function
global function find_min (sequence list, integer start)
-- Search for the minimum value in 'list', beginning at index 'start'.
-- Return the index of that element.
-- Notes: This does not do a recursive compare on sub-sequences.
-- An empty sequence will cause a runtime error.
object temp
integer ret
temp = list[start]
ret = start
for i = start+1 to length(list) do
if compare(temp, list[i]) = 1 then
temp = list[i]
ret = i
end if
end for
return ret
end function
global function find_max (sequence list, integer start)
-- Search for the maximum value in 'list', beginning at index 'start'.
-- Return the index of that element.
-- Notes: This does not do a recursive compare on sub-sequences.
-- An empty sequence will cause a runtime error.
object temp
integer ret
temp = list[start]
ret = start
for i = start+1 to length(list) do
if compare(temp, list[i]) = -1 then
temp = list[i]
ret = i
end if
end for
return ret
end function
global function min (sequence list, integer start)
-- Search for the minimum value in 'list', beginning at index 'start'.
-- Return the value of that element.
-- Notes: This does not do a recursive compare on sub-sequences.
-- An empty sequence will cause a runtime error.
object ret
ret = list[start]
for i = start+1 to length(list) do
if compare(ret, list[i]) = 1 then
ret = list[i]
end if
end for
return ret
end function
global function max (sequence list, integer start)
-- Search for the maximum value in 'list', beginning at index 'start'.
-- Return the value of that element.
-- Notes: This does not do a recursive compare on sub-sequences.
-- An empty sequence will cause a runtime error.
object ret
ret = list[start]
for i = start+1 to length(list) do
if compare(ret, list[i]) = -1 then
ret = list[i]
end if
end for
return ret
end function
global function lesser (object x1, object x2)
-- Return the argument with the lesser value.
-- Note: This does not do a recursive compare on sub-sequences.
if compare(x1, x2) <= 0 then
return x1
else
return x2
end if
end function
global function greater (object x1, object x2)
-- Return the argument with the greater value.
-- Note: This does not do a recursive compare on sub-sequences.
if compare(x1, x2) >= 0 then
return x1
else
return x2
end if
end function
Regards,
Juergen
2. Re: Proposal for 'math.e' (2007-08-07)
- Posted by Colin Taylor <colinetaylor at ?mail.?om>
Aug 07, 2007
-
Last edited Aug 08, 2007
Juergen Luethje wrote:
>
> This is my current (2007-08-07) proposal for a "math.e" standard
> include file, according to the recent discussion here on EUforum.
>
...
>
> type point_pol (object x)
> if sequence(x) and (length(x) = 2)
> and atom(x[1]) and (x[1] >= 0)
> and atom(x[2]) then
> return 1
> end if
> return 0
> end type
>
> global function polar_to_rect (point_pol p)
> -- convert polar coordinates to rectangular coordinates
> -- in : sequence of two atoms: {distance, angle};
> -- 'distance' must be >= 0, 'angle' is in radians
> -- out: sequence of two atoms: {x, y}
> atom distance, angle, x, y
>
> distance = p[1]
> angle = p[2]
> x = distance*cos(angle)
> y = distance*sin(angle)
> return {x, y}
> end function
>
> type point_xy (object x)
> if sequence(x) and (length(x) = 2)
> and atom(x[1])
> and atom(x[2]) then
> return 1
> end if
> return 0
> end type
>
> global function rect_to_polar (point_xy p)
> -- convert rectangular coordinates to polar coordinates
> -- in : sequence of two atoms: {x, y}
> -- out: sequence of two elements: {distance, angle}
> -- - 'distance' is always an atom >= 0
> -- - 'angle' is normally an atom that expresses radians,
> -- and is in the half-closed interval ]-PI,+PI].
> -- If 'distance' equals 0, then 'angle' is {},
> -- meaning that it is undefined in this case.
> object angle
> atom distance, x, y
>
> x = p[1]
> y = p[2]
> distance = sqrt(x*x + y*y)
> if x > 0 then
> angle = arctan(y/x)
> elsif x < 0 then
> if y < 0 then
> angle = arctan(y/x) - PI
> else
> angle = arctan(y/x) + PI
> end if
> else
> if y < 0 then
> angle = -HALF_PI
> elsif y > 0 then
> angle = HALF_PI
> else
> angle = {} -- The angle is undefined in this case.
> end if
> end if
> return {distance, angle}
> end function
>
...
>
> Regards,
> Juergen
Hi Juergen,
I disagree with your solution to the special case rect_to_polar({0,0}). By
setting the undefined angle to {}, you create a situation where the result cannot
be translated back to rectangular coordinates. You are creating a real problem
in order to solve what (imho) is an imagined problem.
Regards, Colin
3. Re: Proposal for 'math.e' (2007-08-07)
Colin Taylor wrote:
> Juergen Luethje wrote:
> >
> > This is my current (2007-08-07) proposal for a "math.e" standard
> > include file, according to the recent discussion here on EUforum.
> >
> ...
> >
> > type point_pol (object x)
> > if sequence(x) and (length(x) = 2)
> > and atom(x[1]) and (x[1] >= 0)
> > and atom(x[2]) then
> > return 1
> > end if
> > return 0
> > end type
> >
> > global function polar_to_rect (point_pol p)
> > -- convert polar coordinates to rectangular coordinates
> > -- in : sequence of two atoms: {distance, angle};
> > -- 'distance' must be >= 0, 'angle' is in radians
> > -- out: sequence of two atoms: {x, y}
> > atom distance, angle, x, y
> >
> > distance = p[1]
> > angle = p[2]
> > x = distance*cos(angle)
> > y = distance*sin(angle)
> > return {x, y}
> > end function
> >
> > type point_xy (object x)
> > if sequence(x) and (length(x) = 2)
> > and atom(x[1])
> > and atom(x[2]) then
> > return 1
> > end if
> > return 0
> > end type
> >
> > global function rect_to_polar (point_xy p)
> > -- convert rectangular coordinates to polar coordinates
> > -- in : sequence of two atoms: {x, y}
> > -- out: sequence of two elements: {distance, angle}
> > -- - 'distance' is always an atom >= 0
> > -- - 'angle' is normally an atom that expresses radians,
> > -- and is in the half-closed interval ]-PI,+PI].
> > -- If 'distance' equals 0, then 'angle' is {},
> > -- meaning that it is undefined in this case.
> > object angle
> > atom distance, x, y
> >
> > x = p[1]
> > y = p[2]
> > distance = sqrt(x*x + y*y)
> > if x > 0 then
> > angle = arctan(y/x)
> > elsif x < 0 then
> > if y < 0 then
> > angle = arctan(y/x) - PI
> > else
> > angle = arctan(y/x) + PI
> > end if
> > else
> > if y < 0 then
> > angle = -HALF_PI
> > elsif y > 0 then
> > angle = HALF_PI
> > else
> > angle = {} -- The angle is undefined in this case.
> > end if
> > end if
> > return {distance, angle}
> > end function
> >
> ...
> >
> > Regards,
> > Juergen
>
> Hi Juergen,
>
> I disagree with your solution to the special case rect_to_polar({0,0}). By
> setting the undefined angle to {}, you create a situation where the result
> cannot
> be translated back to rectangular coordinates.
Hi Colin,
sorry, I overlooked that.
> You are creating a real problem
> in order to solve what (imho) is an imagined problem.
I think you are right.
So I propose that rect_to_polar({0,0}) should return {0,0}.
Do you agree with that?
Regards,
Juergen
4. Re: Proposal for 'math.e' (2007-08-07)
Juergen Luethje wrote:
>
...
>
> Hi Colin,
>
> sorry, I overlooked that.
>
> > You are creating a real problem
> > in order to solve what (imho) is an imagined problem.
>
> I think you are right.
> So I propose that rect_to_polar({0,0}) should return {0,0}.
> Do you agree with that?
>
> Regards,
> Juergen
Yes, I agree. You can document that when distance = 0, the angle is undefined.
Any program that relies on the value of angle can test for the distance = 0 case.
Regards, Colin
5. Re: Proposal for 'math.e' (2007-08-07)
Hello all,
I'd like to suggest to change the sum() function in the future 'math.e'
standard include file. The current proposal is:
global function sum (sequence list)
-- Return the sum of all elements in 'list'.
atom ret
ret = 0
for i = 1 to length(list) do
ret += list[i]Rob's opinion:
end for
return ret
end function
That means, 'list' currently can only contain atoms.
But it can also be useful to calculate the sum of several sequences
(taking advantage of Euphoria's built-in sequence operations).
Say we have 3 points, given by their x- and y-coordinates, and we want
to get the center of gravity of these points:
object A, B, C, G
A = {-2, 1}
B = { 5, 0}
C = { 0, 5}
G = sum({A,B,C}) / 3 -- calculate center of gravity
? G -- prints {1,2}; Cool, no?
IMHO there is a problem with this suggestion, though.
If by accident one of the variables is an atom, say
C = 7
then the program will not crash, but the sum() function will return
a result that is probably not intended. In order to avoid such a silent
bug, my proposal is to use an appropriate type-check for the parameter
of sum(). So my new proposal is:
type sequence_of_a_xor_s (object x)
-- A sequence whose top-level elements are either only atoms or only
-- sequences (or which is empty).
integer object_type
if atom(x) then
return 0
end if
if length(x) = 0 then
return 1
end if
object_type = atom(x[1])
for i = 2 to length(x) do
if object_type != atom(x[i]) then
return 0
end if
end for
return 1
end type
global function sum (sequence_of_a_xor_s list)
-- Return the sum of all elements in 'list'.
-- Note: This does not do a recursive sum of sub-sequences.
object ret
if length(list) = 0 then
return 0
end if
ret = list[1]
for i = 2 to length(list) do
ret += list[i]
end for
return ret
end function
Regards,
Juergen
6. Re: Proposal for 'math.e' (2007-08-07)
Juergen Luethje wrote:
>
> Hello all,
>
> I'd like to suggest to change the sum() function in the future 'math.e'
> standard include file. The current proposal is:
> }}}
<eucode>
> global function sum (sequence list)
> -- Return the sum of all elements in 'list'.
> atom ret
>
> ret = 0
> for i = 1 to length(list) do
> ret += list[i]Rob's opinion:
> end for
> return ret
> end function
> </eucode>
{{{
> That means, 'list' currently can only contain atoms.
>
> But it can also be useful to calculate the sum of several sequences
> (taking advantage of Euphoria's built-in sequence operations).
> Say we have 3 points, given by their x- and y-coordinates, and we want
> to get the center of gravity of these points:
> }}}
<eucode>
> object A, B, C, G
>
> A = {-2, 1}
> B = { 5, 0}
> C = { 0, 5}
>
> G = sum({A,B,C}) / 3 -- calculate center of gravity
> ? G -- prints {1,2}; Cool, no?
> </eucode>
{{{
> IMHO there is a problem with this suggestion, though.
> If by accident one of the variables is an atom, say
> C = 7
> then the program will not crash, but the sum() function will return
> a result that is probably not intended. In order to avoid such a silent
> bug, my proposal is to use an appropriate type-check for the parameter
> of sum(). So my new proposal is:
> }}}
<eucode>
> type sequence_of_a_xor_s (object x)
> -- A sequence whose top-level elements are either only atoms or only
> -- sequences (or which is empty).
> integer object_type
>
> if atom(x) then
> return 0
> end if
>
> if length(x) = 0 then
> return 1
> end if
>
> object_type = atom(x[1])
> for i = 2 to length(x) do
> if object_type != atom(x[i]) then
> return 0
> end if
> end for
>
> return 1
> end type
>
> global function sum (sequence_of_a_xor_s list)
> -- Return the sum of all elements in 'list'.
> -- Note: This does not do a recursive sum of sub-sequences.
> object ret
>
> if length(list) = 0 then
> return 0
> end if
>
> ret = list[1]
> for i = 2 to length(list) do
> ret += list[i]
> end for
> return ret
> end function
> </eucode>
{{{
> Regards,
> Juergen
The above doesn't address the case where, not less accidentally, some of the
sequences being added have different lengths.
I'd suggest the following replacement for the type check:
function lengt(object x)
if atom(x) then
return -1 -- not a length
else
return length(x)
end if
end function
type sequence_of_addables (object x)
-- A sequence whose top-level elements are either only atoms or only
-- sequences (or which is empty); the sequences should have the same length.
integer object_type
object_type = compare(x,{})
if object_type < 1 then
return not object_type -- atom -> 0, {} -> 1
end if
object_type = lengt(x[1])
for i = 2 to length(x) do
if object_type != lengt(x[i]) then
return 0
end if
end for
return 1
end type
and change the argument type in sum() of course.
CChris
7. Re: Proposal for 'math.e' (2007-08-07)
CChris wrote:
> Juergen Luethje wrote:
> >
> > Hello all,
> >
> > I'd like to suggest to change the sum() function in the future 'math.e'
> > standard include file. The current proposal is:
> > }}}
<eucode>
> > global function sum (sequence list)
> > -- Return the sum of all elements in 'list'.
> > atom ret
> >
> > ret = 0
> > for i = 1 to length(list) do
> > ret += list[i]Rob's opinion:
> > end for
> > return ret
> > end function
> > </eucode>
{{{
> > That means, 'list' currently can only contain atoms.
> >
> > But it can also be useful to calculate the sum of several sequences
> > (taking advantage of Euphoria's built-in sequence operations).
> > Say we have 3 points, given by their x- and y-coordinates, and we want
> > to get the center of gravity of these points:
> > }}}
<eucode>
> > object A, B, C, G
> >
> > A = {-2, 1}
> > B = { 5, 0}
> > C = { 0, 5}
> >
> > G = sum({A,B,C}) / 3 -- calculate center of gravity
> > ? G -- prints {1,2}; Cool, no?
> > </eucode>
{{{
> > IMHO there is a problem with this suggestion, though.
> > If by accident one of the variables is an atom, say
> > C = 7
> > then the program will not crash, but the sum() function will return
> > a result that is probably not intended. In order to avoid such a silent
> > bug, my proposal is to use an appropriate type-check for the parameter
> > of sum(). So my new proposal is:
> > }}}
<eucode>
> > type sequence_of_a_xor_s (object x)
> > -- A sequence whose top-level elements are either only atoms or only
> > -- sequences (or which is empty).
> > integer object_type
> >
> > if atom(x) then
> > return 0
> > end if
> >
> > if length(x) = 0 then
> > return 1
> > end if
> >
> > object_type = atom(x[1])
> > for i = 2 to length(x) do
> > if object_type != atom(x[i]) then
> > return 0
> > end if
> > end for
> >
> > return 1
> > end type
> >
> > global function sum (sequence_of_a_xor_s list)
> > -- Return the sum of all elements in 'list'.
> > -- Note: This does not do a recursive sum of sub-sequences.
> > object ret
> >
> > if length(list) = 0 then
> > return 0
> > end if
> >
> > ret = list[1]
> > for i = 2 to length(list) do
> > ret += list[i]
> > end for
> > return ret
> > end function
> > </eucode>
{{{
> > Regards,
> > Juergen
>
> The above doesn't address the case where, not less accidentally, some of the
> sequences being added have different lengths.
That's intended.
At first I wanted to include this in the type-check, that's why I
developed the type 'homogeneous_sequence'. Later I realized that
it's not necessary to check the sequence lengths explicitely, because
Euphoria does check that anyway, when it performs sequence operations.
Please try it. Use something like
? sum({ {1}, {2,3} })
with my proposed type-cheking. The program will crash. If we'll
explicitely type-check for different sequence lengths, then the
program won't do anything else but crash.
Regards,
Juergen
> I'd suggest the following replacement for the type check:
> }}}
<eucode>
> function lengt(object x)
> if atom(x) then
> return -1 -- not a length
> else
> return length(x)
> end if
> end function
>
> type sequence_of_addables (object x)
> -- A sequence whose top-level elements are either only atoms or only
> -- sequences (or which is empty); the sequences should have the same
> length.
> integer object_type
>
> object_type = compare(x,{})
> if object_type < 1 then
> return not object_type -- atom -> 0, {} -> 1
> end if
>
> object_type = lengt(x[1])
> for i = 2 to length(x) do
> if object_type != lengt(x[i]) then
> return 0
> end if
> end for
>
> return 1
> end type
> </eucode>
{{{
>
> and change the argument type in sum() of course.
>
> CChris