www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.math2 proposal

reply "Ben Hinkle" <bhinkle mathworks.com> writes:
Right now in std.math2 there are several "min" and "max" and various other 
high-level math overloads. For example min(int x, int y), min(long x, long 
y), min(real x, real y),
min(int[] x), min(long[] x), min(real[] x).
I'd like to replace those with
  template min(T:T[]) {
    T min(T[] x) {
      T res = T.max;
      foreach(T val; x) {
        if (val < res) res = val;
      }
      return res;
    }
  }
  template min(T) {
    T min(T[] x ...) { return min!(T[])(x); }
   }

User code would look like, for example,
  min!(int)(10, 20)
  min!(short)(x, 10)
  max!(double)(10, 33.33, -PI, 2*PI, 0);
  int[] nums = ...;
  int bound = max!(int[])(nums);
With inlining the simple calls with just two elements should be just as fast 
as exising solutions.
I could also see using templates for sum, too, and maybe even sqr, sign and 
abs. I suppose documenting std.math2 would also be worthwhile. Then again 
putting some of the more useful definitions in std.math and leaving 
std.math2 be is another option. I don't know what the original design of 
math/math2 was for.

Thoughts?
Aug 18 2005
next sibling parent reply "Charles" <noone nowhere.com> writes:
Sounds good to me , I use my own handrolled templates for these already , as
a part of phobos would be much better ... and math2 isn't document or even
mentioned in the official docs , be nice to have a refrence for it.

Not sure why math2 even exists, why not throw them all in math ?

Charlie

"Ben Hinkle" <bhinkle mathworks.com> wrote in message
news:de2hb4$1pf$1 digitaldaemon.com...
 Right now in std.math2 there are several "min" and "max" and various other
 high-level math overloads. For example min(int x, int y), min(long x, long
 y), min(real x, real y),
 min(int[] x), min(long[] x), min(real[] x).
 I'd like to replace those with
   template min(T:T[]) {
     T min(T[] x) {
       T res = T.max;
       foreach(T val; x) {
         if (val < res) res = val;
       }
       return res;
     }
   }
   template min(T) {
     T min(T[] x ...) { return min!(T[])(x); }
    }

 User code would look like, for example,
   min!(int)(10, 20)
   min!(short)(x, 10)
   max!(double)(10, 33.33, -PI, 2*PI, 0);
   int[] nums = ...;
   int bound = max!(int[])(nums);
 With inlining the simple calls with just two elements should be just as

 as exising solutions.
 I could also see using templates for sum, too, and maybe even sqr, sign

 abs. I suppose documenting std.math2 would also be worthwhile. Then again
 putting some of the more useful definitions in std.math and leaving
 std.math2 be is another option. I don't know what the original design of
 math/math2 was for.

 Thoughts?

Aug 18 2005
next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Charles" <noone nowhere.com> wrote in message 
news:de3034$fhe$1 digitaldaemon.com...
 Sounds good to me , I use my own handrolled templates for these already , 
 as
 a part of phobos would be much better ... and math2 isn't document or even
 mentioned in the official docs , be nice to have a refrence for it.

 Not sure why math2 even exists, why not throw them all in math ?

Yeah, I'm thinking that would make sense, too. Here's the contents of math2 for those who haven't opened it lately. A couple of ideas come to mind: 1) deprecate feq now that we have feqrel 2) deprecate toString and atof since they are in std.string 3) deprecate conversions between angle units 4) move everything else to std.math with templates for the overloaded functions bit feq(real a, real b) bit feq(real a, real b, real eps) int abs(int n) long abs(long n) real abs(real n) int sqr(int n) long sqr(long n) real sqr(real n) real trunc(real n) real frac(real n) real poly(real x, real[] coefficients) int sign(int n) int sign(long n) int sign(real n) real cycle2deg(real c) real cycle2rad(real c) real cycle2grad(real c) real deg2cycle(real d) real deg2rad(real d) real deg2grad(real d) real rad2deg(real r) real rad2cycle(real r) real rad2grad(real r) real grad2deg(real g) real grad2cycle(real g) real grad2rad(real g) real avg(real[] n) int sum(int[] n) long sum(long[] n) real sum(real[] n) int min(int[] n) long min(long[] n) real min(real[] n) int min(int a, int b) long min(long a, long b) real min(real a, real b) int max(int[] n) long max(long[] n) real max(real[] n) int max(int a, int b) long max(long a, long b) real max(real a, real b) real acot(real x) real asec(real x) real acosec(real x) real cot(real x) real sec(real x) real cosec(real x) real coth(real x) real sech(real x) real cosech(real x) real acosh(real x) real asinh(real x) real atanh(real x) real acoth(real x) real asech(real x) real acosech(real x) real atof(char[] s) char[] toString(real x)
Aug 18 2005
parent reply Don Clugston <dac nospam.com.au> writes:
I agree. The date in math2 is 2002 -- things have changed a lot since 
then. Some of that stuff is a bit bizarre -- who uses grads????
abs() is the most important thing in there, but why isn't it an intrinsic?

A compiler can do tricks like:

// eliminates a poorly predictable branch
int fastAbs(int x)
{
   asm {
    mov EAX, [x];
    cdq;
    xor EAX, EDX;
    sub EAX, EDX;
    }
}
(not generally applicable because it only works for EAX, EDX, won't 
optimise well in general).
I could imagine a processor including abs in its instruction set.
But as a minimum it should be in std.math.

A comment about your templates for max,min:
with sum and avg, when using floats/doubles, you should be accumulating 
in a real. Likewise ints should accumulate in a long.
Might be nice to also have a template for these cases which returned 
'long' for integral types,  'real' for floats, ireal for imaginary, 
creal for complex, and passed user defined types straight through.

[[ ... except, if your type was (say) MyPolyType!(double), you might
want to upgrade it to a MyPolyType!(real) ... nice idea, but probably 
never required. ]]

Does sqr() deserve to be in there? It doesn't add any value that I can see.

Also I'm not sure that sign() is useful since we have signbit() in 
std.math -- when would you use it?

Likewise, all the auxilliary trig funcs (cot, sec, cosec, coth,..) can 
be trivially generated from the standard ones. I think that unless 
there's some accuracy subtleties or a speed advantage, it's hard to 
justify something so simple. Especially since the use of asm in that 
case probably _inhibits_ optimisation! eg 2.5 * cot(x) should become
2.5/tan(x).

-Don.



Ben Hinkle wrote:
 "Charles" <noone nowhere.com> wrote in message 
 news:de3034$fhe$1 digitaldaemon.com...
 
Sounds good to me , I use my own handrolled templates for these already , 
as
a part of phobos would be much better ... and math2 isn't document or even
mentioned in the official docs , be nice to have a refrence for it.

Not sure why math2 even exists, why not throw them all in math ?

Yeah, I'm thinking that would make sense, too. Here's the contents of math2 for those who haven't opened it lately. A couple of ideas come to mind: 1) deprecate feq now that we have feqrel 2) deprecate toString and atof since they are in std.string 3) deprecate conversions between angle units 4) move everything else to std.math with templates for the overloaded functions bit feq(real a, real b) bit feq(real a, real b, real eps) int abs(int n) long abs(long n) real abs(real n) int sqr(int n) long sqr(long n) real sqr(real n) real trunc(real n) real frac(real n) real poly(real x, real[] coefficients) int sign(int n) int sign(long n) int sign(real n) real cycle2deg(real c) real cycle2rad(real c) real cycle2grad(real c) real deg2cycle(real d) real deg2rad(real d) real deg2grad(real d) real rad2deg(real r) real rad2cycle(real r) real rad2grad(real r) real grad2deg(real g) real grad2cycle(real g) real grad2rad(real g) real avg(real[] n) int sum(int[] n) long sum(long[] n) real sum(real[] n) int min(int[] n) long min(long[] n) real min(real[] n) int min(int a, int b) long min(long a, long b) real min(real a, real b) int max(int[] n) long max(long[] n) real max(real[] n) int max(int a, int b) long max(long a, long b) real max(real a, real b) real acot(real x) real asec(real x) real acosec(real x) real cot(real x) real sec(real x) real cosec(real x) real coth(real x) real sech(real x) real cosech(real x) real acosh(real x) real asinh(real x) real atanh(real x) real acoth(real x) real asech(real x) real acosech(real x) real atof(char[] s) char[] toString(real x)

Aug 18 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Don Clugston" <dac nospam.com.au> wrote in message 
news:de387d$msf$1 digitaldaemon.com...
I agree. The date in math2 is 2002 -- things have changed a lot since then. 
Some of that stuff is a bit bizarre -- who uses grads????
 abs() is the most important thing in there, but why isn't it an intrinsic?

 A compiler can do tricks like:

 // eliminates a poorly predictable branch
 int fastAbs(int x)
 {
   asm {
    mov EAX, [x];
    cdq;
    xor EAX, EDX;
    sub EAX, EDX;
    }
 }
 (not generally applicable because it only works for EAX, EDX, won't 
 optimise well in general).
 I could imagine a processor including abs in its instruction set.
 But as a minimum it should be in std.math.

ok
 A comment about your templates for max,min:
 with sum and avg, when using floats/doubles, you should be accumulating in 
 a real. Likewise ints should accumulate in a long.
 Might be nice to also have a template for these cases which returned 
 'long' for integral types,  'real' for floats, ireal for imaginary, creal 
 for complex, and passed user defined types straight through.

Good point. I was actually thinking avg would stay as a real-only function since averaging a bunch of ints will probably result in a non-int. Maybe it can support float/double but the rest of std.math deals with reals. How about only min/max work with generic types, sum works with anything and returns long or real and avg only works with real.
 [[ ... except, if your type was (say) MyPolyType!(double), you might
 want to upgrade it to a MyPolyType!(real) ... nice idea, but probably 
 never required. ]]

 Does sqr() deserve to be in there? It doesn't add any value that I can 
 see.

yeah - the only thing I could think of was that expr needs to be written out twice expr*expr instead of once sqr(expr). But that's not worth a whole function, I agree.
 Also I'm not sure that sign() is useful since we have signbit() in 
 std.math -- when would you use it?

I'm on the fence with that since taking the sign of an int shouldn't have to go through signbit(real). To me this is on the save level as abs().
 Likewise, all the auxilliary trig funcs (cot, sec, cosec, coth,..) can be 
 trivially generated from the standard ones. I think that unless there's 
 some accuracy subtleties or a speed advantage, it's hard to justify 
 something so simple. Especially since the use of asm in that case probably 
 _inhibits_ optimisation! eg 2.5 * cot(x) should become
 2.5/tan(x).

agreed. deprecate.
 -Don.



 Ben Hinkle wrote:
 "Charles" <noone nowhere.com> wrote in message 
 news:de3034$fhe$1 digitaldaemon.com...

Sounds good to me , I use my own handrolled templates for these already , 
as
a part of phobos would be much better ... and math2 isn't document or 
even
mentioned in the official docs , be nice to have a refrence for it.

Not sure why math2 even exists, why not throw them all in math ?

Yeah, I'm thinking that would make sense, too. Here's the contents of math2 for those who haven't opened it lately. A couple of ideas come to mind: 1) deprecate feq now that we have feqrel 2) deprecate toString and atof since they are in std.string 3) deprecate conversions between angle units 4) move everything else to std.math with templates for the overloaded functions bit feq(real a, real b) bit feq(real a, real b, real eps) int abs(int n) long abs(long n) real abs(real n) int sqr(int n) long sqr(long n) real sqr(real n) real trunc(real n) real frac(real n) real poly(real x, real[] coefficients) int sign(int n) int sign(long n) int sign(real n) real cycle2deg(real c) real cycle2rad(real c) real cycle2grad(real c) real deg2cycle(real d) real deg2rad(real d) real deg2grad(real d) real rad2deg(real r) real rad2cycle(real r) real rad2grad(real r) real grad2deg(real g) real grad2cycle(real g) real grad2rad(real g) real avg(real[] n) int sum(int[] n) long sum(long[] n) real sum(real[] n) int min(int[] n) long min(long[] n) real min(real[] n) int min(int a, int b) long min(long a, long b) real min(real a, real b) int max(int[] n) long max(long[] n) real max(real[] n) int max(int a, int b) long max(long a, long b) real max(real a, real b) real acot(real x) real asec(real x) real acosec(real x) real cot(real x) real sec(real x) real cosec(real x) real coth(real x) real sech(real x) real cosech(real x) real acosh(real x) real asinh(real x) real atanh(real x) real acoth(real x) real asech(real x) real acosech(real x) real atof(char[] s) char[] toString(real x)


Aug 18 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Ben Hinkle wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:de387d$msf$1 digitaldaemon.com...
 
I agree. The date in math2 is 2002 -- things have changed a lot since then. 
Some of that stuff is a bit bizarre -- who uses grads????
abs() is the most important thing in there, but why isn't it an intrinsic?

A compiler can do tricks like:

// eliminates a poorly predictable branch
int fastAbs(int x)
{
  asm {
   mov EAX, [x];
   cdq;
   xor EAX, EDX;
   sub EAX, EDX;
   }
}
(not generally applicable because it only works for EAX, EDX, won't 
optimise well in general).
I could imagine a processor including abs in its instruction set.
But as a minimum it should be in std.math.

ok
A comment about your templates for max,min:
with sum and avg, when using floats/doubles, you should be accumulating in 
a real. Likewise ints should accumulate in a long.
Might be nice to also have a template for these cases which returned 
'long' for integral types,  'real' for floats, ireal for imaginary, creal 
for complex, and passed user defined types straight through.

Good point. I was actually thinking avg would stay as a real-only function since averaging a bunch of ints will probably result in a non-int. Maybe it can support float/double but the rest of std.math deals with reals. How about only min/max work with generic types, sum works with anything and returns long or real and avg only works with real.

Let the user decide with a second parameter.
 
[[ ... except, if your type was (say) MyPolyType!(double), you might
want to upgrade it to a MyPolyType!(real) ... nice idea, but probably 
never required. ]]

Does sqr() deserve to be in there? It doesn't add any value that I can 
see.

yeah - the only thing I could think of was that expr needs to be written out twice expr*expr instead of once sqr(expr). But that's not worth a whole function, I agree.

Any time you need to write something twice you've introduced the potential for error (during maintenance, or just by having a side effect) and made the code harder to read. It stays.
Likewise, all the auxilliary trig funcs (cot, sec, cosec, coth,..) can be 
trivially generated from the standard ones. I think that unless there's 
some accuracy subtleties or a speed advantage, it's hard to justify 
something so simple. Especially since the use of asm in that case probably 
_inhibits_ optimisation! eg 2.5 * cot(x) should become
2.5/tan(x).

agreed. deprecate.

So you're going to deprecate all the trigonometric functions, since they are all simple relationships on pi? These do provide an important purpose: they tell the reader what he's doing, instead of telling him how he's doing it. Would you really see "1 / sin (x)" and go "oh right he's getting the cosecant", particularly if that's embedded in a much larger calculation?
Aug 18 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
A comment about your templates for max,min:
with sum and avg, when using floats/doubles, you should be accumulating 
in a real. Likewise ints should accumulate in a long.
Might be nice to also have a template for these cases which returned 
'long' for integral types,  'real' for floats, ireal for imaginary, creal 
for complex, and passed user defined types straight through.

Good point. I was actually thinking avg would stay as a real-only function since averaging a bunch of ints will probably result in a non-int. Maybe it can support float/double but the rest of std.math deals with reals. How about only min/max work with generic types, sum works with anything and returns long or real and avg only works with real.

Let the user decide with a second parameter.

What do you mean?
Does sqr() deserve to be in there? It doesn't add any value that I can 
see.

yeah - the only thing I could think of was that expr needs to be written out twice expr*expr instead of once sqr(expr). But that's not worth a whole function, I agree.

Any time you need to write something twice you've introduced the potential for error (during maintenance, or just by having a side effect) and made the code harder to read. It stays.

sheesh - you're a flexible one aren't ya. :-) fine, it stays (hands on hips)
Likewise, all the auxilliary trig funcs (cot, sec, cosec, coth,..) can be 
trivially generated from the standard ones. I think that unless there's 
some accuracy subtleties or a speed advantage, it's hard to justify 
something so simple. Especially since the use of asm in that case 
probably _inhibits_ optimisation! eg 2.5 * cot(x) should become
2.5/tan(x).

agreed. deprecate.

So you're going to deprecate all the trigonometric functions, since they are all simple relationships on pi? These do provide an important purpose: they tell the reader what he's doing, instead of telling him how he's doing it. Would you really see "1 / sin (x)" and go "oh right he's getting the cosecant", particularly if that's embedded in a much larger calculation?

I agree cosecant and friends do come up but hyperbolic arccotangent? yikes. But since you're standing up for them I'll let Don see if he cares enough to argue (ps - I don't really care if they stay or go).
Aug 18 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Ben Hinkle wrote:
A comment about your templates for max,min:
with sum and avg, when using floats/doubles, you should be accumulating 
in a real. Likewise ints should accumulate in a long.
Might be nice to also have a template for these cases which returned 
'long' for integral types,  'real' for floats, ireal for imaginary, creal 
for complex, and passed user defined types straight through.

Good point. I was actually thinking avg would stay as a real-only function since averaging a bunch of ints will probably result in a non-int. Maybe it can support float/double but the rest of std.math deals with reals. How about only min/max work with generic types, sum works with anything and returns long or real and avg only works with real.

Let the user decide with a second parameter.

What do you mean?
Does sqr() deserve to be in there? It doesn't add any value that I can 
see.

yeah - the only thing I could think of was that expr needs to be written out twice expr*expr instead of once sqr(expr). But that's not worth a whole function, I agree.

Any time you need to write something twice you've introduced the potential for error (during maintenance, or just by having a side effect) and made the code harder to read. It stays.


Well, you could write pow(expr, 2). A problem I have with sqr() is that some languages use "sqr" instead of "sqrt" (VB perhaps?).
So you're going to deprecate all the trigonometric functions, since they 
are all simple relationships on pi?  


No, there are subtleties involved in some functions. These do provide an important
purpose: they tell the reader what he's doing, instead of telling him how 
he's doing it.  Would you really see "1 / sin (x)" and go "oh right he's 
getting the cosecant", particularly if that's embedded in a much larger 
calculation?


Personally, I always hate seeing "cosec" in a formula, I always translate it into 1/sin in my head ... but that's just my prejudice. I don't really have a problem with any of these functions being included, but there is a philosophical question... how many functions will there eventually be in std.math ? I can see three options: (a) Follow C, and C++ with a minimalist approach to std.math, and deprecate those functions. (b) Include them in std.math, even if it means std.math will eventually become very large. (c) Put trivial derived functions in a seperate file. Maybe std.math2 ? :-) In any case, I do think it is important to have a policy, so that it is reasonably predictable where functions will be, rather than an ad-hoc approach.
Aug 18 2005
next sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 I don't really have a problem with any of these functions being included, 
 but there is a philosophical question...
 how many functions will there eventually be in std.math ?

 I can see three options:
 (a) Follow C, and C++ with a minimalist approach to std.math, and 
 deprecate those functions.
 (b) Include them in std.math, even if it means std.math will eventually 
 become very large.
 (c) Put trivial derived functions in a seperate file. Maybe std.math2 ? 
 :-)

 In any case, I do think it is important to have a policy, so that it is 
 reasonably predictable where functions will be, rather than an ad-hoc 
 approach.

That's a good question. Another concern I have is where to put the complex functions. Note std.math has sqrt(creal) already (I imagine to nicely overload the other sqrts) and more overloads will come soon. Given D's overloading rules I think std.math is going to get big. I am in the process of adding abs(creal) which just calls hypot. I'm not going to bother to add overloads for the pure-imaginary types but for completeness I wouldn't be surprised if they end up in there. If the complex functions went into another module I'd say they should have a "c" prefix since mixing real and creal will be common when doing complex math. I think a big std.math is ok, though. The compiler is fast. The doc is manageable. And one doesn't have to guess if a function is trivial or not enough to import std.math or std.math2.
Aug 19 2005
prev sibling next sibling parent Dave <Dave_member pathlink.com> writes:
In article <de3ma1$1325$1 digitaldaemon.com>, Don Clugston says...
Ben Hinkle wrote:
A comment about your templates for max,min:
with sum and avg, when using floats/doubles, you should be accumulating 
in a real. Likewise ints should accumulate in a long.
Might be nice to also have a template for these cases which returned 
'long' for integral types,  'real' for floats, ireal for imaginary, creal 
for complex, and passed user defined types straight through.

Good point. I was actually thinking avg would stay as a real-only function since averaging a bunch of ints will probably result in a non-int. Maybe it can support float/double but the rest of std.math deals with reals. How about only min/max work with generic types, sum works with anything and returns long or real and avg only works with real.

Let the user decide with a second parameter.

What do you mean?
Does sqr() deserve to be in there? It doesn't add any value that I can 
see.

yeah - the only thing I could think of was that expr needs to be written out twice expr*expr instead of once sqr(expr). But that's not worth a whole function, I agree.

Any time you need to write something twice you've introduced the potential for error (during maintenance, or just by having a side effect) and made the code harder to read. It stays.


Well, you could write pow(expr, 2). A problem I have with sqr() is that some languages use "sqr" instead of "sqrt" (VB perhaps?).
So you're going to deprecate all the trigonometric functions, since they 
are all simple relationships on pi?  


No, there are subtleties involved in some functions. These do provide an important
purpose: they tell the reader what he's doing, instead of telling him how 
he's doing it.  Would you really see "1 / sin (x)" and go "oh right he's 
getting the cosecant", particularly if that's embedded in a much larger 
calculation?


Personally, I always hate seeing "cosec" in a formula, I always translate it into 1/sin in my head ... but that's just my prejudice. I don't really have a problem with any of these functions being included, but there is a philosophical question... how many functions will there eventually be in std.math ? I can see three options: (a) Follow C, and C++ with a minimalist approach to std.math, and deprecate those functions. (b) Include them in std.math, even if it means std.math will eventually become very large. (c) Put trivial derived functions in a seperate file. Maybe std.math2 ? :-) In any case, I do think it is important to have a policy, so that it is reasonably predictable where functions will be, rather than an ad-hoc approach.

(c) makes sense to me.. I think probably the original idea behind math2 was: std.math was supposed to contain basically what a C programmer would expect in math.h, and then 'std.math2' would contain other things that really belong in a math lib. somewhere (for example, abs is in C's stdlib.h). I like your idea - get everything important in std.math regardless of what a C progammer would intuit, and put the less often used functions in std.math2.
Aug 19 2005
prev sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 (b) Include them in std.math, even if it means std.math will eventually 
 become very large.

What actually would be really cool is having generic algorithms to compute the various special functions (probably just using something brute-force like the power series) and use those for complex and arbitrary-precision versions (see http://home.comcast.net/~benhinkle/gmp-d/). That would be really cool to have all the standard math functions (including min/max and friends) that just work for ints, reals, creals and arbitrary-precision floats. I should add arbitrary-precision complex to gmp-d and then that could work, too. Anyway... I'm just dreaming out loud here about what a *nice* std.math would look like. :-)
Aug 19 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Ben Hinkle wrote:
(b) Include them in std.math, even if it means std.math will eventually 
become very large.

What actually would be really cool is having generic algorithms to compute the various special functions (probably just using something brute-force like the power series) and use those for complex and arbitrary-precision versions (see http://home.comcast.net/~benhinkle/gmp-d/). That would be really cool to have all the standard math functions (including min/max and friends) that just work for ints, reals, creals and arbitrary-precision floats. I should add arbitrary-precision complex to gmp-d and then that could work, too. Anyway... I'm just dreaming out loud here about what a *nice* std.math would look like. :-)

I'm not entirely sure that the generic functions like min(), sum() belong in std.math. But, if there were generic algorithms for power series, I might change my mind. My "gut feel" is that std.math is for functions that operate on and return real numbers, std.complex or std.cmath would operate on complex or imaginary numbers. Template functions like "min" could be applied in non-mathematical contexts (eg, you might want to find the smallest FunkyWidget), One possibility is to follow the STL from C++ and create a std.algorithm. Of course the STL made some very strange decisions, accumulate() isn't with the other functions, for example. ---- With Walters comment about Pavel's copyright, I suggest that std.math2 be moved to etc for the time being. Or at least, don't bother documenting it. Just throw abs() into std.math, it's important and trivial.
Aug 21 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Don Clugston" <dac nospam.com.au> wrote in message 
news:deb35e$1rre$1 digitaldaemon.com...
 Ben Hinkle wrote:
(b) Include them in std.math, even if it means std.math will eventually 
become very large.

What actually would be really cool is having generic algorithms to compute the various special functions (probably just using something brute-force like the power series) and use those for complex and arbitrary-precision versions (see http://home.comcast.net/~benhinkle/gmp-d/). That would be really cool to have all the standard math functions (including min/max and friends) that just work for ints, reals, creals and arbitrary-precision floats. I should add arbitrary-precision complex to gmp-d and then that could work, too. Anyway... I'm just dreaming out loud here about what a *nice* std.math would look like. :-)

I'm not entirely sure that the generic functions like min(), sum() belong in std.math. But, if there were generic algorithms for power series, I might change my mind.

Where would you put min/sum/etc?
 My "gut feel" is that std.math is for functions that operate on and return 
 real numbers, std.complex or std.cmath would operate on complex or 
 imaginary numbers.

The problem with using multiple modules is that D's overloading rules are very strict and so user code can't import both std.math and std.complex and freely mix calls to functions overloaded between modules. So for example if sin was defined in std.math and std.complex then writing sin(x) would be illegal - it would have to be explicitly written as std.math.sin(x) or std.complex.sin(x) or users would have to alias sin in the current module. So with D if there's a chance two modules will be used at the same time they should not overload each other. Personally I think using std.math for "all things mathematical" is natural. Even mathematical operations like min/max that can be applied to anything that overloads < or > or whatever. It's still most commonly used as a mathematical operation. Maybe we need std.math and std.mathwannabes.
 Template functions like "min" could be applied in non-mathematical 
 contexts (eg, you might want to find the smallest FunkyWidget),
 One possibility is to follow the STL from C++ and create a std.algorithm. 
 Of course the STL made some very strange decisions,
 accumulate() isn't with the other functions, for example.

Sounds reasonable - and std.math can import whatever has the min/max/etc so that user code doesn't have to import lots of stuff just to do basic math operations.
Aug 22 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Ben Hinkle wrote:

 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:deb35e$1rre$1 digitaldaemon.com...
 
Ben Hinkle wrote:

(b) Include them in std.math, even if it means std.math will eventually 
become very large.

What actually would be really cool is having generic algorithms to compute the various special functions (probably just using something brute-force like the power series) and use those for complex and arbitrary-precision versions (see http://home.comcast.net/~benhinkle/gmp-d/). That would be really cool to have all the standard math functions (including min/max and friends) that just work for ints, reals, creals and arbitrary-precision floats. I should add arbitrary-precision complex to gmp-d and then that could work, too. Anyway... I'm just dreaming out loud here about what a *nice* std.math would look like. :-)

I'm not entirely sure that the generic functions like min(), sum() belong in std.math. But, if there were generic algorithms for power series, I might change my mind.

Where would you put min/sum/etc?

These functions switch on the traits of a comparable type - there are also traits for arrays, strings, numerics, integrals, fractionals and complex (that's where std.math comes in), and others to work with. A library organisation could have it as: module std.tcomparable; template TComparable(T) { T min(T[] list...) { assert (list.length); T result = list[0]; foreach (T item; list[1..$]) if (item < result) result = item; return result; } } But I don't think it's worth doing this to Phobos, because it's such a large restructuring. By the way, that's the only implementation of min you need - you can pass an array to a variadics-array-taking function.
My "gut feel" is that std.math is for functions that operate on and return 
real numbers, std.complex or std.cmath would operate on complex or 
imaginary numbers.

The problem with using multiple modules is that D's overloading rules are very strict and so user code can't import both std.math and std.complex and freely mix calls to functions overloaded between modules. So for example if sin was defined in std.math and std.complex then writing sin(x) would be illegal - it would have to be explicitly written as std.math.sin(x) or std.complex.sin(x) or users would have to alias sin in the current module. So with D if there's a chance two modules will be used at the same time they should not overload each other.

Yeah, it's a real pain in the ass how D makes writing functions a mandatory part of its engineering but supports them so horribly. The same problem applies to mixins. The claim is that this way prevents bugs, but what it really does is put pitfalls into developing and maintaining code. D's very simple function overloading will still catch any ambiguous conflict.
 Personally I think using std.math for "all things mathematical" is natural. 
 Even mathematical operations like min/max that can be applied to anything 
 that overloads < or > or whatever. It's still most commonly used as a 
 mathematical operation. Maybe we need std.math and std.mathwannabes.
 
 
Template functions like "min" could be applied in non-mathematical 
contexts (eg, you might want to find the smallest FunkyWidget),
One possibility is to follow the STL from C++ and create a std.algorithm. 
Of course the STL made some very strange decisions,
accumulate() isn't with the other functions, for example.

Sounds reasonable - and std.math can import whatever has the min/max/etc so that user code doesn't have to import lots of stuff just to do basic math operations.

Aug 22 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 Where would you put min/sum/etc?

These functions switch on the traits of a comparable type - there are also traits for arrays, strings, numerics, integrals, fractionals and complex (that's where std.math comes in), and others to work with. A library organisation could have it as: module std.tcomparable; template TComparable(T) { T min(T[] list...) { assert (list.length); T result = list[0]; foreach (T item; list[1..$]) if (item < result) result = item; return result; } } But I don't think it's worth doing this to Phobos, because it's such a large restructuring.

I don't follow - what large restructuring? Aren't we just talking about std.math2?
 By the way, that's the only implementation of min you need - you can pass 
 an array to a variadics-array-taking function.

Neat. I didn't know that.
Aug 22 2005
parent Burton Radons <burton-radons smocky.com> writes:
Ben Hinkle wrote:
Where would you put min/sum/etc?

These functions switch on the traits of a comparable type - there are also traits for arrays, strings, numerics, integrals, fractionals and complex (that's where std.math comes in), and others to work with. A library organisation could have it as: module std.tcomparable; template TComparable(T) { T min(T[] list...) { assert (list.length); T result = list[0]; foreach (T item; list[1..$]) if (item < result) result = item; return result; } } But I don't think it's worth doing this to Phobos, because it's such a large restructuring.

I don't follow - what large restructuring? Aren't we just talking about std.math2?

I mean that min/max is part of a much broader set of template mixins that have specialised, stunted forms all throughout Phobos; std.string is a specialisation of a string mixin, std.math is a fractional mixin (specifically a specialisation on the floating-point types to use the intrinsics - the general form would use approximating algorithms), array properties are a miniscule subset of an array mixin. It would be very valuable to have those templates in a standard library; they'd have richer functionality and they would work with user types. However, I don't think it's worth trying to reform Phobos to such an extent. I'm not talking about a template library like MinTL - instead one that purely functions with the builtin types.
Aug 23 2005
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"Charles" <noone nowhere.com> wrote in message
news:de3034$fhe$1 digitaldaemon.com...
 Not sure why math2 even exists, why not throw them all in math ?

Because of Pavel's copyright on it. The stuff needs to be reimplemented to get around it, or math2 needs to be moved into etc.
Aug 21 2005
prev sibling parent Don Clugston <dac nospam.com.au> writes:
While we're on the subject, here's another quick function which exposes 
the x86 "fsincos" asm instruction. C99 considered including cis() but 
eventually rejected  it; but I think the argument is stronger for D 
since (a) D has mandated the use of IEEE floats (and therefore x86 is a 
very significant platform), and (b) complex reals are more strongly 
embedded in the language.

Might belong in std.cmath or std.complex, rather than std.math.
-------------------------------------------------

SOME DOC CHANGES FOR std.math:

The documentation for sin(x) in VC++2003 states:
"If x is greater than or equal to 2^63, or less than or equal to -2^63
a loss of significance in the result occurs."
That's a massive understatement. In fact, outside this range the results 
from the fsin, fcos instructions are nonsense. sin(1e20) returns 1e20.

So the docs for sin(x), cos(x), cis(x) should state:
"sin(x) is valid only for abs(x) < 0x1p+63 (approx 9e18). Outside this 
range, the results are undefined."

tan(x) seems to be unaffected by this.

Also, in the "constants" section, it might be helpful to add the line:
"These are the closest representable numbers to the corresponding 
mathematical constant. Because the precision is not infinite, results
may not be what you expect. For example, sin(2*PI) will not be 
<i>exactly<i> zero; instead, it will be about 1e-20 on most systems."

There should be one line descriptions of what each constant is, as well.
Trivial, but it would bring the docs closer to 1.0.

-Don.
------------------------------------------------------
//  cis(theta) = cos(theta) + sin(theta)i.
version (X86)
{
     // On x86, this is almost twice as fast as sin(x) + 1.0i*cos(x).
     creal cis(real x)
     {
      asm {
          fld x;
          fsincos;
          fxch st(1), st(0);
       }
     }
} else {

     creal cis(real x)
     {
       return cos(x) + sin(x)*1i;
     }

}

unittest {
     assert(cis(0)==1+0i);
     assert(cis(1.3e5)==cos(1.3e5)+sin(1.3e5)*1i);
     creal c = cis(real.nan);
     assert(isnan(c.re) && isnan(c.im));
     c = cis(real.infinity);
     assert(isnan(c.re) && isnan(c.im));
}
Aug 21 2005