January 01, 2014
On Wednesday, 1 January 2014 at 19:55:58 UTC, Joseph Rushton Wakeling wrote:
> On Wednesday, 1 January 2014 at 12:29:35 UTC, Stewart Gordon wrote:
>> [...] why do you need an appropriate application in order not to have this arbitrary restriction?  Or have I missed something?
>
> There are binary operations on complex numbers where the only sensible outcome seems to be non-integral real and imaginary parts. Addition, subtraction and multiplication are OK with integral types, but division really seems unpleasant to implement absent floating point, exponentiation even more so.
>
> I imagine there are ways to resolve this, but it certainly simplifies implementation to assume floating-point, and absent a compelling application there is not much reason to avoid that simplification.

I agree completely.  This is the main reason why I chose to explicitly disallow integral types when I wrote std.complex.

>> It isn't just integer types.  Somebody might want to use complex with a library (fixed-point, arbitrary precision, decimal, etc.) numeric type.
>> Fractal generators, for example, are likely to use this a lot.
>
> I agree that such any numeric type that effectively models a real number should be supported. In principle it ought to be sufficient to check that the required "floating-point-ish" operations (including sin and cos) are supported, plus maybe some tweaks to how internal temporary values are handled.

Agreed.  Any "floating point-like" library type which is to be supported by std.complex must either have cos(), sin(), hypot(), etc. as member functions (since std.complex cannot import non-std modules), or the type needs to be supported by std.math as well.  If so, std.math is the place to start, not std.complex.
January 01, 2014
On Wednesday, 1 January 2014 at 12:29:35 UTC, Stewart Gordon wrote:
>  [...]
>
> Or even more exotically, use Complex!(Complex!real) to implement hypercomplex numbers.

This is an extremely marginal use case.  Currently Complex!(Complex!T) folds to Complex!T.  I thought this was specified in the module documentation, but if it was, it's been removed.  It is explained in a comment in the source code, though:

/*  Makes Complex!(Complex!T) fold to Complex!T.

    The rationale for this is that just like the real line is a
    subspace of the complex plane, the complex plane is a subspace
    of itself. Example of usage:
    ---
    Complex!T addI(T)(T x)
    {
        return x + Complex!T(0.0, 1.0);
    }
    ---
    The above will work if T is both real and complex.
*/
template Complex(T) if (is(T R == Complex!R))
{
    alias T Complex;
}

January 01, 2014
On Friday, 20 December 2013 at 16:26:00 UTC, Joseph Rushton Wakeling wrote:
> On 26/11/13 17:28, Andrei Alexandrescu wrote:
>> On 11/26/13 3:22 AM, Joseph Rushton Wakeling wrote:
>>> So, as other people have suggested, really the only thing we can
>>> reasonably do is to define a separate Imaginary type
>>
>> I agree. Let's move forward with this.
>
> Provisional code:
> https://github.com/D-Programming-Language/phobos/pull/1797
>
> I also realized there was no explicit issue report, so created one:
> https://d.puremagic.com/issues/show_bug.cgi?id=11787

I'm not 100% convinced that a pure imaginary type is the way to go.  You may be interested in reading a previous discussion, where the subject of pure imaginary number support is brought up:

  http://forum.dlang.org/thread/flsf9u$jf$1@digitalmars.com

Let me quote some of the replies in that thread:

On Monday, 7 January 2008 at 08:05:48 UTC, Don Clugston wrote:
> I think the argument for pure imaginary types is extremely weak. They are very annoying to work with, because they aren't closed under multiplication. This is a nasty property to have in a built-in type.
>
> Suppose you're writing a generic function product(T)(T[]) which returns T[0]*T[1]*T[2]*... What's the return type?
> Suppose T is idouble. Then if the number of elements in T is odd, the return type should be idouble, but if it's even, the return type should be double!
> You could promote it to complex with a construction like typeof(1?T*T:T), but that's inefficient, and negates most of the benefits of having a imaginary type.


On Tuesday, 8 January 2008 at 03:13:34 UTC, Bill Baxter wrote:
> The bottom line is that there really is no useful mathematics that can be performed using only imaginary numbers.  If to do anything more than add, subtract, or multiply by a (real!) scalar they become complex.  If you have any imaginary numbers in a numerical code it means you're working in the complex plane.
January 01, 2014
On 11/26/2013 3:22 AM, Joseph Rushton Wakeling wrote:
> (It also leaves std.complex' behaviour incompatible with std::complex and other
> library implementations, though I guess that's less of a concern so long as we
> think what std.complex does is right.)

Making it behave differently than others would be a disaster.
January 02, 2014
On Wednesday, 1 January 2014 at 23:54:05 UTC, Walter Bright wrote:
> On 11/26/2013 3:22 AM, Joseph Rushton Wakeling wrote:
>> (It also leaves std.complex' behaviour incompatible with std::complex and other
>> library implementations, though I guess that's less of a concern so long as we
>> think what std.complex does is right.)
>
> Making it behave differently than others would be a disaster.

Just as well that I was speaking hypothetically about things that _could_ be done, rather than _should_ be ... :-)

January 02, 2014
On Wednesday, 1 January 2014 at 23:32:58 UTC, Lars T. Kyllingstad wrote:
> I'm not 100% convinced that a pure imaginary type is the way to go.  You may be interested in reading a previous discussion, where the subject of pure imaginary number support is brought up:
>
>   http://forum.dlang.org/thread/flsf9u$jf$1@digitalmars.com

I have read through that thread already, but thank you for bringing it up -- it raises some issues that deserve an answer.

My own thoughts on the matter go something like this:

   * There are calculations involving "pair of reals" implementations of complex
     numbers that generate spuriously incorrect results. They're corner cases,
     but they exist.

   * A purely imaginary type allows those calculations to be performed
     correctly. It also allows more precise calculations in general for cases
     where the real part of a complex number is known to be 0.

   * The idea of allowing imaginary literals but promoting them to complex when
     written to a variable sounds attractive but we can't discount the
     possibility that people will want to pass imaginary literals to functions
     or otherwise store them in variables, and later use them; if they are
     promoted to complex, calculations done with them may have lesser precision
     or even generate the aforementioned spurious results. I don't think simply
     writing to a variable should cause loss of precision like this.

   * You can do calculations involving purely real-valued numbers and complex
     numbers and not run into the same issues, because purely real values are
     supported. So you should be able to do the same with purely imaginary
     numbers.

   * The lack of closure under various operations is annoying but not
     insurmountable.

I should add that as far as I'm concerned what I want is simply to find the best possible way to represent and use purely imaginary numbers in D. I don't mind if, having done that, the code winds up being rejected, as long as the exercise proves useful.

Of course, an alternative would be to tweak the internals of Complex so that it can represent "purely real" and "purely imaginary" types precisely.

I'm writing from a phone now so it's not really convenient to explain at length, but I'll try to expand on the above in the next days.
January 02, 2014
On 1/1/14 3:12 PM, Lars T. Kyllingstad wrote:
> On Wednesday, 1 January 2014 at 12:29:35 UTC, Stewart Gordon wrote:
>>  [...]
>>
>> Or even more exotically, use Complex!(Complex!real) to implement
>> hypercomplex numbers.
>
> This is an extremely marginal use case.  Currently Complex!(Complex!T)
> folds to Complex!T.

What was the motivation?

Andrei


January 02, 2014
On Thursday, 2 January 2014 at 02:59:05 UTC, Andrei Alexandrescu
wrote:
> On 1/1/14 3:12 PM, Lars T. Kyllingstad wrote:
>> Currently Complex!(Complex!T)
>> folds to Complex!T.
>
> What was the motivation?

To simplify generic code, where you have a function that takes
both real and complex arguments and returns a complex number,
e.g.:

   Complex!T fun(T)(T x) {
       return x * someComplexNumber;
   }

In the above, T may be real or complex.
January 02, 2014
On Tuesday, 26 November 2013 at 12:52:30 UTC, John Colvin wrote:
>
> It seems to me that one really shouldn't special case for absolute values when dealing with floating point.

Why not? std.math and std.mathspecial do it all the time.
January 02, 2014
On Tuesday, 26 November 2013 at 13:32:22 UTC, John Colvin wrote:
>
> Just had a chat with one of our local numerics experts: He agreed that hidden special casing that breaks standards compliance is bad and that whatever the solution was it should be explicit.

I don't think we should worry too much about standards compliance. A library Complex type is quite different from a hardware floating-point type.