Skip to content

Commit 1d1e6e8

Browse files
fix: non-finite numbers in various functions (#28)
* New test cases * Multiply/2 test cases * Divide/2 tests * Some pow/2 tests * Add remaining pow/2 tests * atan2 tests and fix * Fix for add/2 * Subtract/2 fix * Fix multiply/2 * Fix divide/2 * Fix atan2/2 * Fix multiply/2 doctest * fix: make tests pass * fix: ci * fix: elixir version ci --------- Co-authored-by: Paulo Valente <[email protected]>
1 parent dcdc7f5 commit 1d1e6e8

File tree

3 files changed

+376
-69
lines changed

3 files changed

+376
-69
lines changed

.github/workflows/ci.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jobs:
1111
strategy:
1212
fail-fast: false
1313
matrix:
14-
elixir: ["1.12.0"]
15-
otp: ["24.0"]
14+
elixir: ["1.18.0"]
15+
otp: ["27.0"]
1616
env:
1717
MIX_ENV: test
1818
steps:
@@ -22,7 +22,7 @@ jobs:
2222
otp-version: ${{ matrix.otp }}
2323
elixir-version: ${{ matrix.elixir }}
2424
- name: Retrieve dependencies cache
25-
uses: actions/cache@v1
25+
uses: actions/cache@v4
2626
id: mix-cache # id to use in retrieve action
2727
with:
2828
path: deps
@@ -42,8 +42,8 @@ jobs:
4242
strategy:
4343
fail-fast: false
4444
matrix:
45-
elixir: ["1.12.0"]
46-
otp: ["24.0"]
45+
elixir: ["1.18.0"]
46+
otp: ["27.0"]
4747
env:
4848
MIX_ENV: test
4949
steps:
@@ -57,7 +57,7 @@ jobs:
5757
otp-version: ${{ matrix.otp }}
5858
elixir-version: ${{ matrix.elixir }}
5959
- name: Retrieve dependencies cache
60-
uses: actions/cache@v1
60+
uses: actions/cache@v4
6161
id: mix-cache # id to use in retrieve action
6262
with:
6363
path: deps

lib/complex.ex

+140-49
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,12 @@ defmodule Complex do
389389
@spec add(t | number | non_finite_number, t | number | non_finite_number) ::
390390
t | number | non_finite_number
391391

392+
def add(z, %Complex{re: re, im: im}) when z in [:infinity, :neg_infinity, :nan],
393+
do: Complex.new(add(z, re), im)
394+
395+
def add(%Complex{re: re, im: im}, z) when z in [:infinity, :neg_infinity, :nan],
396+
do: Complex.new(add(z, re), im)
397+
392398
def add(:nan, _), do: :nan
393399
def add(_, :nan), do: :nan
394400

@@ -431,6 +437,12 @@ defmodule Complex do
431437
@spec subtract(t | number | non_finite_number, t | number | non_finite_number) ::
432438
t | number | non_finite_number
433439

440+
def subtract(z, %Complex{re: re, im: im}) when z in [:infinity, :neg_infinity, :nan],
441+
do: Complex.new(subtract(z, re), negate(im))
442+
443+
def subtract(%Complex{re: re, im: im}, z) when z in [:infinity, :neg_infinity, :nan],
444+
do: Complex.new(subtract(re, z), im)
445+
434446
def subtract(:nan, _), do: :nan
435447
def subtract(_, :nan), do: :nan
436448
def subtract(:infinity, :infinity), do: :nan
@@ -472,12 +484,22 @@ defmodule Complex do
472484
%Complex{im: 6.0, re: 3.0}
473485
474486
iex> Complex.multiply(-2, Complex.new(:infinity, :neg_infinity))
475-
%Complex{im: :infinity, re: :neg_infinity}
487+
%Complex{im: :nan, re: :nan}
476488
477489
"""
478490
@spec multiply(t | number | non_finite_number, t | number | non_finite_number) ::
479491
t | number | non_finite_number
480492

493+
def multiply(x, c = %Complex{re: _re, im: _im}) when is_non_finite_number(x) or is_number(x) do
494+
z = new(x, 0)
495+
multiply(z, c)
496+
end
497+
498+
def multiply(c = %Complex{re: _re, im: _im}, x) when is_non_finite_number(x) or is_number(x) do
499+
z = new(x, 0)
500+
multiply(c, z)
501+
end
502+
481503
def multiply(:infinity, right) when is_number(right) and right > 0, do: :infinity
482504
def multiply(:infinity, right) when right == 0, do: :nan
483505
def multiply(:infinity, right) when is_number(right) and right < 0, do: :neg_infinity
@@ -500,28 +522,6 @@ defmodule Complex do
500522
def multiply(:infinity, :neg_infinity), do: :neg_infinity
501523
def multiply(:infinity, :infinity), do: :infinity
502524

503-
def multiply(x, %Complex{re: re, im: im}) when is_non_finite_number(x) or is_number(x) do
504-
new(multiply(re, x), multiply(im, x))
505-
end
506-
507-
def multiply(%Complex{re: re, im: im}, x) when is_non_finite_number(x) or is_number(x) do
508-
new(multiply(re, x), multiply(im, x))
509-
end
510-
511-
def multiply(%Complex{re: re_l, im: im_l}, %Complex{re: re_r, im: im_r}) when im_r == 0 do
512-
new(multiply(re_r, re_l), multiply(re_r, im_l))
513-
end
514-
515-
def multiply(%Complex{re: re_l, im: im_l}, %Complex{re: re_r, im: im_r}) when re_r == 0 do
516-
re_result =
517-
case multiply(im_l, im_r) do
518-
result when result == 0 -> 0
519-
result -> negate(result)
520-
end
521-
522-
new(re_result, multiply(re_l, im_r))
523-
end
524-
525525
def multiply(left, right) when is_number(left) and is_number(right), do: left * right
526526

527527
def multiply(left, right) do
@@ -569,33 +569,43 @@ defmodule Complex do
569569
def divide(x, y) when is_non_finite_number(x) and is_non_finite_number(y), do: :nan
570570
def divide(x, y) when is_non_finite_number(x) and is_number(y) and y >= 0, do: x
571571
def divide(x, y) when x == 0 and y == 0, do: :nan
572-
def divide(:nan, _), do: :nan
573-
def divide(_, :nan), do: :nan
572+
def divide(:nan, a) when is_number(a), do: :nan
573+
def divide(a, :nan) when is_number(a), do: :nan
574574
def divide(:infinity, y) when is_number(y) and y < 0, do: :neg_infinity
575575
def divide(:neg_infinity, y) when is_number(y) and y < 0, do: :infinity
576-
def divide(_, :infinity), do: 0
577-
def divide(_, :neg_infinity), do: 0
576+
def divide(a, :infinity) when is_number(a), do: 0
577+
def divide(a, :neg_infinity) when is_number(a), do: 0
578578

579579
def divide(x, y) when is_number(x) and is_number(y), do: x / y
580580

581+
def divide(n, b) when is_number(n) and b in [:infinity, :neg_infinity] do
582+
0
583+
end
584+
585+
def divide(n, %Complex{re: re_r, im: im_r})
586+
when is_number(n) and re_r in [:infinity, :neg_infinity] and im_r == 0 do
587+
new(0, 0)
588+
end
589+
590+
def divide(%Complex{re: re, im: im}, b)
591+
when is_number(re) and is_number(im) and b in [:infinity, :neg_infinity] do
592+
new(0, 0)
593+
end
594+
595+
def divide(%Complex{re: re, im: im}, %Complex{re: re_r, im: im_r})
596+
when is_number(re) and is_number(im) and re_r in [:infinity, :neg_infinity] and im_r == 0 do
597+
new(0, 0)
598+
end
599+
581600
def divide(x, y) do
582601
%Complex{re: r1, im: i1} = as_complex(x)
583602
%Complex{re: r2, im: i2} = as_complex(y)
584603

585-
cond do
586-
i2 == 0 ->
587-
new(divide(r1, r2), divide(i1, r2))
588-
589-
r2 == 0 ->
590-
new(divide(i1, i2), negate(divide(r1, i2)))
591-
592-
true ->
593-
num_re = add(multiply(r1, r2), multiply(i1, i2))
594-
num_im = subtract(multiply(i1, r2), multiply(r1, i2))
595-
den = add(square(r2), square(i2))
604+
num_re = add(multiply(r1, r2), multiply(i1, i2))
605+
num_im = subtract(multiply(i1, r2), multiply(r1, i2))
606+
den = add(square(r2), square(i2))
596607

597-
new(divide(num_re, den), divide(num_im, den))
598-
end
608+
new(divide(num_re, den), divide(num_im, den))
599609
end
600610

601611
@doc """
@@ -939,13 +949,49 @@ defmodule Complex do
939949
def log10(z)
940950

941951
def log10(:infinity), do: :infinity
942-
def log10(:neg_infinity), do: divide(log(:neg_infinity), :math.log(10))
952+
953+
def log10(:neg_infinity) do
954+
new(:infinity, :math.pi() / :math.log(10))
955+
end
956+
943957
def log10(:nan), do: :nan
944958
def log10(n) when is_number(n) and n == 0, do: :neg_infinity
945959
def log10(n) when is_number(n) and n < 0, do: :nan
946960

947961
def log10(n) when is_number(n), do: :math.log10(n)
948962

963+
def log10(%Complex{re: :infinity, im: im}) when is_number(im) do
964+
new(:infinity, 0)
965+
end
966+
967+
def log10(%Complex{re: :neg_infinity, im: im}) when is_number(im) do
968+
new(:infinity, :math.pi() / :math.log(10))
969+
end
970+
971+
def log10(%Complex{im: :infinity, re: re}) when is_number(re) do
972+
new(:infinity, :math.pi() / (2 * :math.log(10)))
973+
end
974+
975+
def log10(%Complex{im: :neg_infinity, re: re}) when is_number(re) do
976+
new(:infinity, -:math.pi() / (2 * :math.log(10)))
977+
end
978+
979+
def log10(%Complex{re: :infinity, im: :infinity}) do
980+
new(:infinity, :math.pi() / (4 * :math.log(10)))
981+
end
982+
983+
def log10(%Complex{re: :infinity, im: :neg_infinity}) do
984+
new(:infinity, -:math.pi() / (4 * :math.log(10)))
985+
end
986+
987+
def log10(%Complex{re: :neg_infinity, im: :neg_infinity}) do
988+
new(:infinity, -3 * :math.pi() / (4 * :math.log(10)))
989+
end
990+
991+
def log10(%Complex{re: :neg_infinity, im: :infinity}) do
992+
new(:infinity, 3 * :math.pi() / (4 * :math.log(10)))
993+
end
994+
949995
def log10(z = %Complex{}) do
950996
divide(log(z), new(:math.log(10.0), 0.0))
951997
end
@@ -975,6 +1021,38 @@ defmodule Complex do
9751021

9761022
def log2(n) when is_number(n), do: :math.log2(n)
9771023

1024+
def log2(%Complex{re: :infinity, im: im}) when is_number(im) do
1025+
new(:infinity, 0)
1026+
end
1027+
1028+
def log2(%Complex{re: :neg_infinity, im: im}) when is_number(im) do
1029+
new(:infinity, :math.pi() / :math.log(2))
1030+
end
1031+
1032+
def log2(%Complex{im: :infinity, re: re}) when is_number(re) do
1033+
new(:infinity, :math.pi() / (2 * :math.log(2)))
1034+
end
1035+
1036+
def log2(%Complex{im: :neg_infinity, re: re}) when is_number(re) do
1037+
new(:infinity, -:math.pi() / (2 * :math.log(2)))
1038+
end
1039+
1040+
def log2(%Complex{re: :infinity, im: :infinity}) do
1041+
new(:infinity, :math.pi() / (4 * :math.log(2)))
1042+
end
1043+
1044+
def log2(%Complex{re: :infinity, im: :neg_infinity}) do
1045+
new(:infinity, -:math.pi() / (4 * :math.log(2)))
1046+
end
1047+
1048+
def log2(%Complex{re: :neg_infinity, im: :neg_infinity}) do
1049+
new(:infinity, -3 * :math.pi() / (4 * :math.log(2)))
1050+
end
1051+
1052+
def log2(%Complex{re: :neg_infinity, im: :infinity}) do
1053+
new(:infinity, 3 * :math.pi() / (4 * :math.log(2)))
1054+
end
1055+
9781056
def log2(z = %Complex{}) do
9791057
divide(log(z), new(:math.log(2.0), 0.0))
9801058
end
@@ -996,6 +1074,18 @@ defmodule Complex do
9961074
@spec pow(t | non_finite_number | number, t | non_finite_number | number) ::
9971075
t | non_finite_number | number
9981076

1077+
def pow(%Complex{re: re, im: im}, :infinity) when re == 0 and im == 0, do: new(0, 0)
1078+
1079+
def pow(%Complex{re: re, im: im}, %Complex{re: :infinity, im: im_r})
1080+
when re == 0 and im == 0 and im_r == 0,
1081+
do: new(0, 0)
1082+
1083+
def pow(z, %Complex{}) when is_non_finite_number(z), do: new(:nan, :nan)
1084+
def pow(%Complex{}, z) when is_non_finite_number(z), do: new(:nan, :nan)
1085+
def pow(%Complex{re: z}, _) when is_non_finite_number(z), do: new(:nan, :nan)
1086+
def pow(%Complex{im: z}, _) when is_non_finite_number(z), do: new(:nan, :nan)
1087+
def pow(_, %Complex{re: z}) when is_non_finite_number(z), do: new(:nan, :nan)
1088+
def pow(_, %Complex{im: z}) when is_non_finite_number(z), do: new(:nan, :nan)
9991089
def pow(:nan, _), do: :nan
10001090
def pow(_, :nan), do: :nan
10011091

@@ -1015,9 +1105,8 @@ defmodule Complex do
10151105
def pow(:neg_infinity, y) when is_number(y) and y < 0, do: 0
10161106

10171107
def pow(x, :infinity) when x == 0, do: 0
1018-
def pow(%Complex{re: re, im: im}, :infinity) when re == 0 and im == 0, do: 0
10191108
def pow(x, :neg_infinity) when x == 0, do: :infinity
1020-
def pow(%Complex{re: re, im: im}, :neg_infinity) when re == 0 and im == 0, do: :infinity
1109+
def pow(%Complex{re: re, im: im}, :neg_infinity) when re == 0 and im == 0, do: new(:infinity, 0)
10211110
def pow(_, :neg_infinity), do: 0
10221111
def pow(_, :infinity), do: :infinity
10231112

@@ -1308,14 +1397,16 @@ defmodule Complex do
13081397
def atan2(:infinity, :infinity), do: :math.pi() / 4
13091398
def atan2(:infinity, :neg_infinity), do: 3 * :math.pi() / 4
13101399
def atan2(:infinity, :nan), do: :nan
1311-
def atan2(:infinity, _), do: :math.pi() / 2
1400+
def atan2(:infinity, a) when is_number(a), do: :math.pi() / 2
13121401
def atan2(:neg_infinity, :infinity), do: -:math.pi() / 4
13131402
def atan2(:neg_infinity, :neg_infinity), do: -3 * :math.pi() / 4
13141403
def atan2(:neg_infinity, :nan), do: :nan
1315-
def atan2(:neg_infinity, _), do: -:math.pi() / 2
1316-
def atan2(:nan, _), do: :nan
1317-
def atan2(_, :infinity), do: 0
1318-
def atan2(_, :neg_infinity), do: :math.pi()
1404+
def atan2(:neg_infinity, a) when is_number(a), do: -:math.pi() / 2
1405+
def atan2(:nan, a) when is_number(a) or is_non_finite_number(a), do: :nan
1406+
def atan2(a, :nan) when is_number(a) or is_non_finite_number(a), do: :nan
1407+
def atan2(:nan, :nan), do: :nan
1408+
def atan2(a, :infinity) when is_number(a), do: 0
1409+
def atan2(a, :neg_infinity) when is_number(a), do: :math.pi()
13191410

13201411
def atan2(b, a) do
13211412
a = as_complex(a)
@@ -1416,7 +1507,7 @@ defmodule Complex do
14161507
### Examples
14171508
14181509
iex> Complex.asec(Complex.from_polar(2,:math.pi))
1419-
%Complex{im: 0.0, re: 2.0943951023931957}
1510+
%Complex{im: -0.0, re: 2.0943951023931957}
14201511
14211512
iex> Complex.sec(Complex.asec(Complex.new(2,3)))
14221513
%Complex{im: 2.9999999999999982, re: 1.9999999999999987}

0 commit comments

Comments
 (0)