diff --git a/.github/.keepalive b/.github/.keepalive deleted file mode 100644 index 56814fe..0000000 --- a/.github/.keepalive +++ /dev/null @@ -1 +0,0 @@ -2023-07-01T05:20:12.576Z diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0c2727b..2ce52c4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -9,22 +9,22 @@ Bruno Fenzl Christopher Dambamuromo Dominik Moritz Frank Kovacs -Harshita Kalani <95532771+HarshitaKalani@users.noreply.github.com> -James +Harshita Kalani +James Gelok Jithin KS Joey Reed -Jordan-Gallivan <115050475+Jordan-Gallivan@users.noreply.github.com> +Jordan Gallivan <115050475+Jordan-Gallivan@users.noreply.github.com> Joris Labie Justin Dennison -KATTA NAGA NITHIN <88046362+nithinkatta@users.noreply.github.com> -Marcus +Nithin Katta <88046362+nithinkatta@users.noreply.github.com> +Marcus Fantham Matt Cochrane Milan Raj Momtchil Momtchev -Naresh Jagadeesan <37257700+Infinage@users.noreply.github.com> +Naresh Jagadeesan Ognjen Jevremović Philipp Burckhardt -Pranav <85227306+Pranavchiku@users.noreply.github.com> +Pranav Goswami Ricky Reusser Roman Stetsyk <25715951+romanstetsyk@users.noreply.github.com> Ryan Seal @@ -32,7 +32,7 @@ Seyyed Parsa Neshaei Shraddheya Shendre Stephannie Jiménez Gacha Yernar Yergaziyev -dorrin-sot <59933477+dorrin-sot@users.noreply.github.com> -drunken_devv <90555965+amitjimiwal@users.noreply.github.com> +Dorrin Sotoudeh +Amit Jimiwal orimiles5 <97595296+orimiles5@users.noreply.github.com> -rei2hu +rei2hu diff --git a/README.md b/README.md index 97d9070..db51af1 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ limitations under the License. --> -# Signum +# csignum [![NPM version][npm-image]][npm-url] [![Build Status][test-image]][test-url] [![Coverage Status][coverage-image]][coverage-url] -> Evaluate the [signum][signum] function of a complex number. +> Evaluate the [signum][signum] function of a double-precision complex floating-point number. @@ -60,33 +60,41 @@ The [branches.md][branches-url] file summarizes the available branches and displ var csignum = require( '@stdlib/math-base-special-csignum' ); ``` -#### csignum( \[out,] re, im ) +#### csignum( z ) -Evaluates the [signum][signum] function of a `complex` number comprised of a **real** component `re` and an **imaginary** component `im`. +Evaluates the [signum][signum] function of a double-precision complex floating-point number. ```javascript -var v = csignum( -4.2, 5.5 ); -// returns [ -0.6069136033622302, 0.79476781392673 ] +var Complex128 = require( '@stdlib/complex-float64' ); +var real = require( '@stdlib/complex-real' ); +var imag = require( '@stdlib/complex-imag' ); -v = csignum( 0.0, 0.0 ); -// returns [ 0.0, 0.0 ] +var v = csignum( new Complex128( -4.2, 5.5 ) ); +// returns -v = csignum( NaN, NaN ); -// returns [ NaN, NaN ] -``` +var re = real( v ); +// returns -0.6069136033622302 -By default, the function returns real and imaginary components as a two-element `array`. To avoid unnecessary memory allocation, the function supports providing an output (destination) object. +var im = imag( v ); +// returns 0.79476781392673 -```javascript -var Float64Array = require( '@stdlib/array-float64' ); +v = csignum( new Complex128( 0.0, 0.0 ) ); +// returns -var out = new Float64Array( 2 ); +re = real( v ); +// returns 0.0 -var v = csignum( out, -4.2, 5.5 ); -// returns [ -0.6069136033622302, 0.79476781392673 ] +im = imag( v ); +// returns 0.0 -var bool = ( v === out ); -// returns true +v = csignum( new Complex128( NaN, NaN ) ); +// returns + +re = real( v ); +// returns NaN + +im = imag( v ); +// returns NaN ``` @@ -110,26 +118,124 @@ var bool = ( v === out ); ```javascript +var uniform = require( '@stdlib/random-base-uniform' ).factory; var Complex128 = require( '@stdlib/complex-float64' ); -var randu = require( '@stdlib/random-base-randu' ); -var real = require( '@stdlib/complex-real' ); -var imag = require( '@stdlib/complex-imag' ); var csignum = require( '@stdlib/math-base-special-csignum' ); -var re; -var im; +var rand = uniform( -50.0, 50.0 ); + var z; -var o; -var w; var i; - for ( i = 0; i < 100; i++ ) { - re = ( randu()*100.0 ) - 50.0; - im = ( randu()*100.0 ) - 50.0; - z = new Complex128( re, im ); - o = csignum( real(z), imag(z) ); - w = new Complex128( o[ 0 ], o[ 1 ] ); - console.log( 'signum(%s) = %s', z.toString(), w.toString() ); + z = new Complex128( rand(), rand() ); + console.log( 'csignum(%s) = %s', z, csignum( z ) ); +} +``` + + + + + + + +* * * + +
+ +## C APIs + + + +
+ +
+ + + + + +
+ +### Usage + +```c +#include "stdlib/math/base/special/csignum.h" +``` + +#### stdlib_base_csignum( z ) + +Evaluates the [signum][signum] function of a double-precision complex floating-point number. + +```c +#include "stdlib/complex/float64.h" +#include "stdlib/complex/real.h" +#include "stdlib/complex/imag.h" + +stdlib_complex128_t z = stdlib_complex128( -4.2, 5.5 ); + +stdlib_complex128_t out = stdlib_base_csignum( z ); + +double re = stdlib_real( out ); +// returns -0.6069136033622302 + +double im = stdlib_imag( out ); +// returns 0.79476781392673 +``` + +The function accepts the following arguments: + +- **z**: `[in] stdlib_complex128_t` input value. + +```c +stdlib_complex128_t stdlib_base_csignum( const stdlib_complex128_t z ); +``` + +
+ + + + + +
+ +
+ + + + + +
+ +### Examples + +```c +#include "stdlib/math/base/special/csignum.h" +#include "stdlib/complex/float64.h" +#include "stdlib/complex/reim.h" +#include + +int main( void ) { + const stdlib_complex128_t x[] = { + stdlib_complex128( 3.14, 1.5 ), + stdlib_complex128( -3.14, -1.5 ), + stdlib_complex128( 0.0, 0.0 ), + stdlib_complex128( 0.0/0.0, 0.0/0.0 ) + }; + + stdlib_complex128_t v; + stdlib_complex128_t y; + double re1; + double im1; + double re2; + double im2; + int i; + for ( i = 0; i < 4; i++ ) { + v = x[ i ]; + y = stdlib_base_csignum( v ); + stdlib_reim( v, &re1, &im1 ); + stdlib_reim( y, &re2, &im2 ); + printf( "csignum(%lf + %lfi) = %lf + %lfi\n", re1, im1, re2, im2 ); + } } ``` @@ -137,6 +243,10 @@ for ( i = 0; i < 100; i++ ) { +
+ + +
diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js index b4ca389..1f71e2e 100644 --- a/benchmark/benchmark.js +++ b/benchmark/benchmark.js @@ -21,10 +21,11 @@ // MODULES // var bench = require( '@stdlib/bench' ); -var randu = require( '@stdlib/random-base-randu' ); -var isArray = require( '@stdlib/assert-is-array' ); +var uniform = require( '@stdlib/random-base-uniform' ); +var isnan = require( '@stdlib/math-base-assert-is-nan' ); var Complex128 = require( '@stdlib/complex-float64' ); -var cabs = require( '@stdlib/math-base-special-cabs' ); +var real = require( '@stdlib/complex-real' ); +var imag = require( '@stdlib/complex-imag' ); var pkg = require( './../package.json' ).name; var csignum = require( './../lib' ); @@ -32,102 +33,25 @@ var csignum = require( './../lib' ); // MAIN // bench( pkg, function benchmark( b ) { - var re; - var im; + var values; var y; var i; - b.tic(); - for ( i = 0; i < b.iterations; i++ ) { - re = ( randu()*1000.0 ) - 500.0; - im = ( randu()*1000.0 ) - 500.0; - y = csignum( re, im ); - if ( y.length === 0 ) { - b.fail( 'should not be empty' ); - } - } - b.toc(); - if ( !isArray( y ) ) { - b.fail( 'should return an array' ); - } - b.pass( 'benchmark finished' ); - b.end(); -}); - -bench( pkg+'::memory_reuse', function benchmark( b ) { - var out; - var re; - var im; - var y; - var i; - - out = new Array( 2 ); - - b.tic(); - for ( i = 0; i < b.iterations; i++ ) { - re = ( randu()*1000.0 ) - 500.0; - im = ( randu()*1000.0 ) - 500.0; - y = csignum( out, re, im ); - if ( y.length === 0 ) { - b.fail( 'should not be empty' ); - } - } - b.toc(); - if ( !isArray( y ) ) { - b.fail( 'should return an array' ); - } - b.pass( 'benchmark finished' ); - b.end(); -}); - -bench( pkg+'::manual', function benchmark( b ) { - var az; - var re; - var im; - var y; - var i; - - b.tic(); - for ( i = 0; i < b.iterations; i++ ) { - re = ( randu()*1000.0 ) - 500.0; - im = ( randu()*1000.0 ) - 500.0; - az = cabs( new Complex128( re, im ) ); - y = [ re/az, im/az ]; - if ( y.length === 0 ) { - b.fail( 'should not be empty' ); - } - } - b.toc(); - if ( !isArray( y ) ) { - b.fail( 'should return an array' ); - } - b.pass( 'benchmark finished' ); - b.end(); -}); - -bench( pkg+'::manual,memory_reuse', function benchmark( b ) { - var az; - var re; - var im; - var y; - var i; - - y = new Array( 2 ); + values = [ + new Complex128( uniform( -500.0, 500.0 ), uniform( -500.0, 500.0 ) ), + new Complex128( uniform( -500.0, 500.0 ), uniform( -500.0, 500.0 ) ) + ]; b.tic(); for ( i = 0; i < b.iterations; i++ ) { - re = ( randu()*1000.0 ) - 500.0; - im = ( randu()*1000.0 ) - 500.0; - az = cabs( new Complex128( re, im ) ); - y[ 0 ] = re / az; - y[ 1 ] = im / az; - if ( y.length === 0 ) { - b.fail( 'should not be empty' ); + y = csignum( values[ i%values.length ] ); + if ( isnan( real( y ) ) ) { + b.fail( 'should not return NaN' ); } } b.toc(); - if ( !isArray( y ) ) { - b.fail( 'should return an array' ); + if ( isnan( imag( y ) ) ) { + b.fail( 'should not return not NaN' ); } b.pass( 'benchmark finished' ); b.end(); diff --git a/benchmark/benchmark.native.js b/benchmark/benchmark.native.js new file mode 100644 index 0000000..ff350c4 --- /dev/null +++ b/benchmark/benchmark.native.js @@ -0,0 +1,66 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var bench = require( '@stdlib/bench' ); +var uniform = require( '@stdlib/random-base-uniform' ); +var isnan = require( '@stdlib/math-base-assert-is-nan' ); +var Complex128 = require( '@stdlib/complex-float64' ); +var real = require( '@stdlib/complex-real' ); +var tryRequire = require( '@stdlib/utils-try-require' ); +var pkg = require( './../package.json' ).name; + + +// VARIABLES // + +var csignum = tryRequire( resolve( __dirname, './../lib/native.js' ) ); +var opts = { + 'skip': ( csignum instanceof Error ) +}; + + +// MAIN // + +bench( pkg+'::native', opts, function benchmark( b ) { + var values; + var y; + var i; + + values = [ + new Complex128( uniform( -500.0, 500.0 ), uniform( -500.0, 500.0 ) ), + new Complex128( uniform( -500.0, 500.0 ), uniform( -500.0, 500.0 ) ) + ]; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + y = csignum( values[ i%values.length ] ); + if ( isnan( real( y ) ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( real( y ) ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); +}); diff --git a/benchmark/c/native/Makefile b/benchmark/c/native/Makefile new file mode 100644 index 0000000..7f6bbc4 --- /dev/null +++ b/benchmark/c/native/Makefile @@ -0,0 +1,146 @@ +#/ +# @license Apache-2.0 +# +# Copyright (c) 2021 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + +# VARIABLES # + +ifndef VERBOSE + QUIET := @ +else + QUIET := +endif + +# Determine the OS ([1][1], [2][2]). +# +# [1]: https://en.wikipedia.org/wiki/Uname#Examples +# [2]: http://stackoverflow.com/a/27776822/2225624 +OS ?= $(shell uname) +ifneq (, $(findstring MINGW,$(OS))) + OS := WINNT +else +ifneq (, $(findstring MSYS,$(OS))) + OS := WINNT +else +ifneq (, $(findstring CYGWIN,$(OS))) + OS := WINNT +else +ifneq (, $(findstring Windows_NT,$(OS))) + OS := WINNT +endif +endif +endif +endif + +# Define the program used for compiling C source files: +ifdef C_COMPILER + CC := $(C_COMPILER) +else + CC := gcc +endif + +# Define the command-line options when compiling C files: +CFLAGS ?= \ + -std=c99 \ + -O3 \ + -Wall \ + -pedantic + +# Determine whether to generate position independent code ([1][1], [2][2]). +# +# [1]: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options +# [2]: http://stackoverflow.com/questions/5311515/gcc-fpic-option +ifeq ($(OS), WINNT) + fPIC ?= +else + fPIC ?= -fPIC +endif + +# List of includes (e.g., `-I /foo/bar -I /beep/boop/include`): +INCLUDE ?= + +# List of source files: +SOURCE_FILES ?= + +# List of libraries (e.g., `-lopenblas -lpthread`): +LIBRARIES ?= + +# List of library paths (e.g., `-L /foo/bar -L /beep/boop`): +LIBPATH ?= + +# List of C targets: +c_targets := benchmark.out + + +# RULES # + +#/ +# Compiles source files. +# +# @param {string} [C_COMPILER] - C compiler (e.g., `gcc`) +# @param {string} [CFLAGS] - C compiler options +# @param {(string|void)} [fPIC] - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} [INCLUDE] - list of includes (e.g., `-I /foo/bar -I /beep/boop/include`) +# @param {string} [SOURCE_FILES] - list of source files +# @param {string} [LIBPATH] - list of library paths (e.g., `-L /foo/bar -L /beep/boop`) +# @param {string} [LIBRARIES] - list of libraries (e.g., `-lopenblas -lpthread`) +# +# @example +# make +# +# @example +# make all +#/ +all: $(c_targets) + +.PHONY: all + +#/ +# Compiles C source files. +# +# @private +# @param {string} CC - C compiler (e.g., `gcc`) +# @param {string} CFLAGS - C compiler options +# @param {(string|void)} fPIC - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} INCLUDE - list of includes (e.g., `-I /foo/bar`) +# @param {string} SOURCE_FILES - list of source files +# @param {string} LIBPATH - list of library paths (e.g., `-L /foo/bar`) +# @param {string} LIBRARIES - list of libraries (e.g., `-lopenblas`) +#/ +$(c_targets): %.out: %.c + $(QUIET) $(CC) $(CFLAGS) $(fPIC) $(INCLUDE) -o $@ $(SOURCE_FILES) $< $(LIBPATH) -lm $(LIBRARIES) + +#/ +# Runs compiled benchmarks. +# +# @example +# make run +#/ +run: $(c_targets) + $(QUIET) ./$< + +.PHONY: run + +#/ +# Removes generated files. +# +# @example +# make clean +#/ +clean: + $(QUIET) -rm -f *.o *.out + +.PHONY: clean diff --git a/benchmark/c/native/benchmark.c b/benchmark/c/native/benchmark.c new file mode 100644 index 0000000..fd5c8e3 --- /dev/null +++ b/benchmark/c/native/benchmark.c @@ -0,0 +1,144 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* Benchmark `csignum`. +*/ +#include "stdlib/math/base/special/csignum.h" +#include "stdlib/complex/float64.h" +#include "stdlib/complex/reim.h" +#include +#include +#include +#include +#include + +#define NAME "csignum" +#define ITERATIONS 1000000 +#define REPEATS 3 + +/** +* Prints the TAP version. +*/ +void print_version() { + printf( "TAP version 13\n" ); +} + +/** +* Prints the TAP summary. +* +* @param total total number of tests +* @param passing total number of passing tests +*/ +void print_summary( int total, int passing ) { + printf( "#\n" ); + printf( "1..%d\n", total ); // TAP plan + printf( "# total %d\n", total ); + printf( "# pass %d\n", passing ); + printf( "#\n" ); + printf( "# ok\n" ); +} + +/** +* Prints benchmarks results. +* +* @param elapsed elapsed time in seconds +*/ +void print_results( double elapsed ) { + double rate = (double)ITERATIONS / elapsed; + printf( " ---\n" ); + printf( " iterations: %d\n", ITERATIONS ); + printf( " elapsed: %0.9f\n", elapsed ); + printf( " rate: %0.9f\n", rate ); + printf( " ...\n" ); +} + +/** +* Returns a clock time. +* +* @return clock time +*/ +double tic() { + struct timeval now; + gettimeofday( &now, NULL ); + return (double)now.tv_sec + (double)now.tv_usec/1.0e6; +} + +/** +* Generates a random number on the interval [0,1]. +* +* @return random number +*/ +double rand_double() { + int r = rand(); + return (double)r / ( (double)RAND_MAX + 1.0 ); +} + +/** +* Runs a benchmark. +* +* @return elapsed time in seconds +*/ +double benchmark() { + double elapsed; + double re; + double im; + double t; + double v; + int i; + + stdlib_complex128_t x; + stdlib_complex128_t y; + + t = tic(); + for ( i = 0; i < ITERATIONS; i++ ) { + v = ( 1000.0*rand_double() ) - 500.0; + x = stdlib_complex128( v, v ); + y = stdlib_base_csignum( x ); + stdlib_reim( y, &re, &im ); + if ( re != re ) { + printf( "unexpected result\n" ); + break; + } + } + elapsed = tic() - t; + if ( im != im ) { + printf( "unexpected result\n" ); + } + return elapsed; +} + +/** +* Main execution sequence. +*/ +int main( void ) { + double elapsed; + int i; + + // Use the current time to seed the random number generator: + srand( time( NULL ) ); + + print_version(); + for ( i = 0; i < REPEATS; i++ ) { + printf( "# c::native::%s\n", NAME ); + elapsed = benchmark(); + print_results( elapsed ); + printf( "ok %d benchmark finished\n", i+1 ); + } + print_summary( REPEATS, REPEATS ); +} diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..f2b466a --- /dev/null +++ b/binding.gyp @@ -0,0 +1,170 @@ +# @license Apache-2.0 +# +# Copyright (c) 2023 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A `.gyp` file for building a Node.js native add-on. +# +# [1]: https://gyp.gsrc.io/docs/InputFormatReference.md +# [2]: https://gyp.gsrc.io/docs/UserDocumentation.md +{ + # List of files to include in this file: + 'includes': [ + './include.gypi', + ], + + # Define variables to be used throughout the configuration for all targets: + 'variables': { + # Target name should match the add-on export name: + 'addon_target_name%': 'addon', + + # Set variables based on the host OS: + 'conditions': [ + [ + 'OS=="win"', + { + # Define the object file suffix: + 'obj': 'obj', + }, + { + # Define the object file suffix: + 'obj': 'o', + } + ], # end condition (OS=="win") + ], # end conditions + }, # end variables + + # Define compile targets: + 'targets': [ + + # Target to generate an add-on: + { + # The target name should match the add-on export name: + 'target_name': '<(addon_target_name)', + + # Define dependencies: + 'dependencies': [], + + # Define directories which contain relevant include headers: + 'include_dirs': [ + # Local include directory: + '<@(include_dirs)', + ], + + # List of source files: + 'sources': [ + '<@(src_files)', + ], + + # Settings which should be applied when a target's object files are used as linker input: + 'link_settings': { + # Define libraries: + 'libraries': [ + '<@(libraries)', + ], + + # Define library directories: + 'library_dirs': [ + '<@(library_dirs)', + ], + }, + + # C/C++ compiler flags: + 'cflags': [ + # Enable commonly used warning options: + '-Wall', + + # Aggressive optimization: + '-O3', + ], + + # C specific compiler flags: + 'cflags_c': [ + # Specify the C standard to which a program is expected to conform: + '-std=c99', + ], + + # C++ specific compiler flags: + 'cflags_cpp': [ + # Specify the C++ standard to which a program is expected to conform: + '-std=c++11', + ], + + # Linker flags: + 'ldflags': [], + + # Apply conditions based on the host OS: + 'conditions': [ + [ + 'OS=="mac"', + { + # Linker flags: + 'ldflags': [ + '-undefined dynamic_lookup', + '-Wl,-no-pie', + '-Wl,-search_paths_first', + ], + }, + ], # end condition (OS=="mac") + [ + 'OS!="win"', + { + # C/C++ flags: + 'cflags': [ + # Generate platform-independent code: + '-fPIC', + ], + }, + ], # end condition (OS!="win") + ], # end conditions + }, # end target <(addon_target_name) + + # Target to copy a generated add-on to a standard location: + { + 'target_name': 'copy_addon', + + # Declare that the output of this target is not linked: + 'type': 'none', + + # Define dependencies: + 'dependencies': [ + # Require that the add-on be generated before building this target: + '<(addon_target_name)', + ], + + # Define a list of actions: + 'actions': [ + { + 'action_name': 'copy_addon', + 'message': 'Copying addon...', + + # Explicitly list the inputs in the command-line invocation below: + 'inputs': [], + + # Declare the expected outputs: + 'outputs': [ + '<(addon_output_dir)/<(addon_target_name).node', + ], + + # Define the command-line invocation: + 'action': [ + 'cp', + '<(PRODUCT_DIR)/<(addon_target_name).node', + '<(addon_output_dir)/<(addon_target_name).node', + ], + }, + ], # end actions + }, # end target copy_addon + ], # end targets +} diff --git a/docs/repl.txt b/docs/repl.txt index 77234c7..b4caecb 100644 --- a/docs/repl.txt +++ b/docs/repl.txt @@ -1,34 +1,26 @@ -{{alias}}( [out,] re, im ) - Evaluates the signum function of a complex number. +{{alias}}( z ) + Evaluates the signum function of a double-precision complex floating-point + number. Parameters ---------- - out: Array|TypedArray|Object (optional) - Output array. - - re: number - Real component. - - im: number - Imaginary component. + z: Complex128 + Complex number. Returns ------- - out: Array|TypedArray|Object - Function result. + out: Complex128 + Result. Examples -------- - > var out = {{alias}}( -4.2, 5.5 ) - [ -0.6069136033622302, 0.79476781392673 ] - - // Provide an output array: - > out = new {{alias:@stdlib/array/float64}}( 2 ); - > var v = {{alias}}( out, -4.2, 5.5 ) - [ -0.6069136033622302, 0.79476781392673 ] - > var bool = ( v === out ) - true + > var v = {{alias}}( new {{alias:@stdlib/complex/float64}}( -4.2, 5.5 ) ) + + > var re = {{alias:@stdlib/complex/real}}( v ) + -0.6069136033622302 + > var im = {{alias:@stdlib/complex/imag}}( v ) + 0.79476781392673 See Also -------- diff --git a/docs/types/index.d.ts b/docs/types/index.d.ts index 4092201..d7e4d63 100644 --- a/docs/types/index.d.ts +++ b/docs/types/index.d.ts @@ -20,49 +20,29 @@ /// -import { ArrayLike } from '@stdlib/types/array'; +import { Complex128 } from '@stdlib/types/object'; /** -* Evaluates the signum function of a complex number. +* Evaluates the signum function of a double-precision complex floating-point number. * -* @param out - output array -* @param re - real component -* @param im - imaginary component -* @returns real and imaginary components +* @param z - input value +* @returns result * * @example -* var Float32Array = require( `@stdlib/array/float32` ); +* var Complex128 = require( `@stdlib/complex/float64` ); +* var real = require( `@stdlib/complex/real` ); +* var imag = require( `@stdlib/complex/imag` ); * -* var out = new Float32Array( 2 ); +* var v = cceil( new Complex128( -4.2, 5.5 ) ); +* // returns * -* var v = csignum( out, -4.2, 5.5 ); -* // returns [ -0.6069136033622302, 0.79476781392673 ] +* var re = real( v ); +* // returns -0.6069136033622302 * -* var bool = ( v === out ); -* // returns true +* var im = imag( v ); +* // returns 0.79476781392673 */ -declare function csignum( out: ArrayLike, re: number, im: number ): ArrayLike; // tslint-disable-line max-line-length - -/** -* Evaluates the signum function of a complex number. -* -* @param re - real component -* @param im - imaginary component -* @returns real and imaginary components -* -* @example -* var v = csignum( -4.2, 5.5 ); -* // returns [ -0.6069136033622302, 0.79476781392673 ] -* -* @example -* var v = csignum( 0.0, 0.0 ); -* // returns [ 0.0, 0.0 ] -* -* @example -* var v = csignum( NaN, NaN ); -* // returns [ NaN, NaN ] -*/ -declare function csignum( re: number, im: number ): ArrayLike; +declare function csignum( z: Complex128 ): Complex128; // EXPORTS // diff --git a/docs/types/test.ts b/docs/types/test.ts index 9317160..80db1fc 100644 --- a/docs/types/test.ts +++ b/docs/types/test.ts @@ -16,6 +16,7 @@ * limitations under the License. */ +import Complex128 = require( '@stdlib/complex-float64' ); import csignum = require( './index' ); @@ -23,46 +24,22 @@ import csignum = require( './index' ); // The function returns an array of numbers... { - csignum( 5, 3 ); // $ExpectType ArrayLike - csignum( [], 5, 3 ); // $ExpectType ArrayLike + csignum( new Complex128( 1.0, 2.0 ) ); // $ExpectType Complex128 } -// The compiler throws an error if the function is provided a real component which is not a number... +// The compiler throws an error if the function is provided a value other than a complex number... { - csignum( true, 3 ); // $ExpectError - csignum( false, 3 ); // $ExpectError - csignum( null, 3 ); // $ExpectError - csignum( undefined, 3 ); // $ExpectError - csignum( '5', 3 ); // $ExpectError - csignum( [], 3 ); // $ExpectError - csignum( {}, 3 ); // $ExpectError - csignum( ( x: number ): number => x, 3 ); // $ExpectError -} - -// The compiler throws an error if the function is provided an imaginary component which is not a number... -{ - csignum( 5, true ); // $ExpectError - csignum( 5, false ); // $ExpectError - csignum( 5, null ); // $ExpectError - csignum( 5, undefined ); // $ExpectError - csignum( 5, '5' ); // $ExpectError - csignum( 5, [] ); // $ExpectError - csignum( 5, {} ); // $ExpectError - csignum( 5, ( x: number ): number => x ); // $ExpectError -} - -// The compiler throws an error if the function is provided an output array which is not array-like... -{ - csignum( true, 5, 3 ); // $ExpectError - csignum( false, 5, 3 ); // $ExpectError - csignum( 'abc', 5, 3 ); // $ExpectError - csignum( {}, 5, 3 ); // $ExpectError - csignum( ( x: number ): number => x, 5, 3 ); // $ExpectError - csignum( 123, 5, 3 ); // $ExpectError + csignum( true ); // $ExpectError + csignum( false ); // $ExpectError + csignum( null ); // $ExpectError + csignum( undefined ); // $ExpectError + csignum( '5' ); // $ExpectError + csignum( [] ); // $ExpectError + csignum( {} ); // $ExpectError + csignum( ( x: number ): number => x ); // $ExpectError } // The compiler throws an error if the function is provided insufficient arguments... { csignum(); // $ExpectError - csignum( 2 ); // $ExpectError } diff --git a/examples/c/Makefile b/examples/c/Makefile new file mode 100644 index 0000000..f0ae66f --- /dev/null +++ b/examples/c/Makefile @@ -0,0 +1,146 @@ +#/ +# @license Apache-2.0 +# +# Copyright (c) 2023 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + +# VARIABLES # + +ifndef VERBOSE + QUIET := @ +else + QUIET := +endif + +# Determine the OS ([1][1], [2][2]). +# +# [1]: https://en.wikipedia.org/wiki/Uname#Examples +# [2]: http://stackoverflow.com/a/27776822/2225624 +OS ?= $(shell uname) +ifneq (, $(findstring MINGW,$(OS))) + OS := WINNT +else +ifneq (, $(findstring MSYS,$(OS))) + OS := WINNT +else +ifneq (, $(findstring CYGWIN,$(OS))) + OS := WINNT +else +ifneq (, $(findstring Windows_NT,$(OS))) + OS := WINNT +endif +endif +endif +endif + +# Define the program used for compiling C source files: +ifdef C_COMPILER + CC := $(C_COMPILER) +else + CC := gcc +endif + +# Define the command-line options when compiling C files: +CFLAGS ?= \ + -std=c99 \ + -O3 \ + -Wall \ + -pedantic + +# Determine whether to generate position independent code ([1][1], [2][2]). +# +# [1]: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options +# [2]: http://stackoverflow.com/questions/5311515/gcc-fpic-option +ifeq ($(OS), WINNT) + fPIC ?= +else + fPIC ?= -fPIC +endif + +# List of includes (e.g., `-I /foo/bar -I /beep/boop/include`): +INCLUDE ?= + +# List of source files: +SOURCE_FILES ?= + +# List of libraries (e.g., `-lopenblas -lpthread`): +LIBRARIES ?= + +# List of library paths (e.g., `-L /foo/bar -L /beep/boop`): +LIBPATH ?= + +# List of C targets: +c_targets := example.out + + +# RULES # + +#/ +# Compiles source files. +# +# @param {string} [C_COMPILER] - C compiler (e.g., `gcc`) +# @param {string} [CFLAGS] - C compiler options +# @param {(string|void)} [fPIC] - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} [INCLUDE] - list of includes (e.g., `-I /foo/bar -I /beep/boop/include`) +# @param {string} [SOURCE_FILES] - list of source files +# @param {string} [LIBPATH] - list of library paths (e.g., `-L /foo/bar -L /beep/boop`) +# @param {string} [LIBRARIES] - list of libraries (e.g., `-lopenblas -lpthread`) +# +# @example +# make +# +# @example +# make all +#/ +all: $(c_targets) + +.PHONY: all + +#/ +# Compiles C source files. +# +# @private +# @param {string} CC - C compiler (e.g., `gcc`) +# @param {string} CFLAGS - C compiler options +# @param {(string|void)} fPIC - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} INCLUDE - list of includes (e.g., `-I /foo/bar`) +# @param {string} SOURCE_FILES - list of source files +# @param {string} LIBPATH - list of library paths (e.g., `-L /foo/bar`) +# @param {string} LIBRARIES - list of libraries (e.g., `-lopenblas`) +#/ +$(c_targets): %.out: %.c + $(QUIET) $(CC) $(CFLAGS) $(fPIC) $(INCLUDE) -o $@ $(SOURCE_FILES) $< $(LIBPATH) -lm $(LIBRARIES) + +#/ +# Runs compiled examples. +# +# @example +# make run +#/ +run: $(c_targets) + $(QUIET) ./$< + +.PHONY: run + +#/ +# Removes generated files. +# +# @example +# make clean +#/ +clean: + $(QUIET) -rm -f *.o *.out + +.PHONY: clean diff --git a/examples/c/example.c b/examples/c/example.c new file mode 100644 index 0000000..bd35f6d --- /dev/null +++ b/examples/c/example.c @@ -0,0 +1,46 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/math/base/special/csignum.h" +#include "stdlib/complex/float64.h" +#include "stdlib/complex/reim.h" +#include + +int main( void ) { + const stdlib_complex128_t x[] = { + stdlib_complex128( 3.14, 1.5 ), + stdlib_complex128( -3.14, -1.5 ), + stdlib_complex128( 0.0, 0.0 ), + stdlib_complex128( 0.0/0.0, 0.0/0.0 ) + }; + + stdlib_complex128_t v; + stdlib_complex128_t y; + double re1; + double im1; + double re2; + double im2; + int i; + for ( i = 0; i < 4; i++ ) { + v = x[ i ]; + y = stdlib_base_csignum( v ); + stdlib_reim( v, &re1, &im1 ); + stdlib_reim( y, &re2, &im2 ); + printf( "csignum(%lf + %lfi) = %lf + %lfi\n", re1, im1, re2, im2 ); + } +} diff --git a/examples/index.js b/examples/index.js index b679939..45dc03d 100644 --- a/examples/index.js +++ b/examples/index.js @@ -18,24 +18,15 @@ 'use strict'; +var uniform = require( '@stdlib/random-base-uniform' ).factory; var Complex128 = require( '@stdlib/complex-float64' ); -var randu = require( '@stdlib/random-base-randu' ); -var real = require( '@stdlib/complex-real' ); -var imag = require( '@stdlib/complex-imag' ); var csignum = require( './../lib' ); -var re; -var im; +var rand = uniform( -50.0, 50.0 ); + var z; -var o; -var w; var i; - for ( i = 0; i < 100; i++ ) { - re = ( randu()*100.0 ) - 50.0; - im = ( randu()*100.0 ) - 50.0; - z = new Complex128( re, im ); - o = csignum( real(z), imag(z) ); - w = new Complex128( o[ 0 ], o[ 1 ] ); - console.log( 'signum(%s) = %s', z.toString(), w.toString() ); + z = new Complex128( rand(), rand() ); + console.log( 'csignum(%s) = %s', z, csignum( z ) ); } diff --git a/include.gypi b/include.gypi new file mode 100644 index 0000000..32c28e3 --- /dev/null +++ b/include.gypi @@ -0,0 +1,53 @@ +# @license Apache-2.0 +# +# Copyright (c) 2023 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A GYP include file for building a Node.js native add-on. +# +# Main documentation: +# +# [1]: https://gyp.gsrc.io/docs/InputFormatReference.md +# [2]: https://gyp.gsrc.io/docs/UserDocumentation.md +{ + # Define variables to be used throughout the configuration for all targets: + 'variables': { + # Source directory: + 'src_dir': './src', + + # Include directories: + 'include_dirs': [ + ' * -* v = csignum( 0.0, 0.0 ); -* // returns [ 0.0, 0.0 ] +* var re = real( v ); +* // returns -0.6069136033622302 * -* v = csignum( NaN, NaN ); -* // returns [ NaN, NaN ] +* var im = imag( v ); +* // returns 0.79476781392673 * -* @example -* var csignum = require( '@stdlib/math-base-special-csignum' ); +* v = csignum( new Complex128( 0.0, 0.0 ) ); +* // returns +* +* re = real( v ); +* // returns -0.0 +* +* im = imag( v ); +* // returns 0.0 * -* var out = new Array( 2 ); +* v = csignum( new Complex128( NaN, NaN ) ); +* // returns * -* var v = csignum( out, -4.2, 5.5 ); -* // returns [ -0.6069136033622302, 0.79476781392673 ] +* re = real( v ); +* // returns -NaN * -* var bool = ( v === out ); -* // returns true +* im = imag( v ); +* // returns NaN */ // MODULES // diff --git a/lib/main.js b/lib/main.js index ca8ea36..41b0ab1 100644 --- a/lib/main.js +++ b/lib/main.js @@ -20,45 +20,64 @@ // MODULES // -var signum = require( './csignum.js' ); +var Complex128 = require( '@stdlib/complex-float64' ); +var real = require( '@stdlib/complex-real' ); +var imag = require( '@stdlib/complex-imag' ); +var cabs = require( '@stdlib/math-base-special-cabs' ); // MAIN // /** -* Evaluates the signum function of a complex number. +* Evaluates the signum function of a double-precision floating-point complex number. * -* @param {(Array|TypedArray|Object)} [out] - output array -* @param {number} re - real component -* @param {number} im - imaginary component -* @returns {(Array|TypedArray|Object)} function result +* @param {Complex128} z - complex number +* @returns {Complex128} result * * @example -* var v = csignum( -4.2, 5.5 ); -* // returns [ -0.6069136033622302, 0.79476781392673 ] +* var Complex128 = require( '@stdlib/complex-float64' ); +* var real = require( '@stdlib/complex-real' ); +* var imag = require( '@stdlib/complex-imag' ); * -* @example -* var out = new Array( 2 ); +* var v = csignum( new Complex128( -4.2, 5.5 ) ); +* // returns * -* var v = csignum( out, -4.2, 5.5 ); -* // returns [ -0.6069136033622302, 0.79476781392673 ] +* var re = real( v ); +* // returns -0.6069136033622302 * -* var bool = ( v === out ); -* // returns true +* var im = imag( v ); +* // returns 0.79476781392673 * -* @example -* var v = csignum( 0.0, 0.0 ); -* // returns [ 0.0, 0.0 ] +* v = csignum( new Complex128( 0.0, 0.0 ) ); +* // returns * -* @example -* var v = csignum( NaN, NaN ); -* // returns [ NaN, NaN ] +* re = real( v ); +* // returns -0.0 +* +* im = imag( v ); +* // returns 0.0 +* +* v = csignum( new Complex128( NaN, NaN ) ); +* // returns +* +* re = real( v ); +* // returns NaN +* +* im = imag( v ); +* // returns NaN */ -function csignum( out, re, im ) { - if ( arguments.length === 2 ) { - return signum( [ 0.0, 0.0 ], out, re ); +function csignum( z ) { + var re; + var im; + var az; + + az = cabs( z ); + if ( az === 0.0 ) { + return z; } - return signum( out, re, im ); + re = real( z ) / az; + im = imag( z ) / az; + return new Complex128( re, im ); } diff --git a/lib/native.js b/lib/native.js new file mode 100644 index 0000000..fe34b15 --- /dev/null +++ b/lib/native.js @@ -0,0 +1,76 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var Complex128 = require( '@stdlib/complex-float64' ); +var addon = require( './../src/addon.node' ); + + +// MAIN // + +/** +* Evaluates the signum function of a double-precision floating-point complex number. +* +* @private +* @param {Complex128} z - complex number +* @returns {Complex128} result +* +* @example +* var Complex128 = require( '@stdlib/complex-float64' ); +* var real = require( '@stdlib/complex-real' ); +* var imag = require( '@stdlib/complex-imag' ); +* +* var v = csignum( new Complex128( -4.2, 5.5 ) ); +* // returns +* +* var re = real( v ); +* // returns -0.6069136033622302 +* +* var im = imag( v ); +* // returns 0.79476781392673 +* +* v = csignum( new Complex128( 0.0, 0.0 ) ); +* // returns +* +* re = real( v ); +* // returns -0.0 +* +* im = imag( v ); +* // returns 0.0 +* +* v = csignum( new Complex128( NaN, NaN ) ); +* // returns +* +* re = real( v ); +* // returns NaN +* +* im = imag( v ); +* // returns NaN +*/ +function csignum( z ) { + var v = addon( z ); + return new Complex128( v.re, v.im ); +} + + +// EXPORTS // + +module.exports = csignum; diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..da9e927 --- /dev/null +++ b/manifest.json @@ -0,0 +1,79 @@ +{ + "options": { + "task": "build" + }, + "fields": [ + { + "field": "src", + "resolve": true, + "relative": true + }, + { + "field": "include", + "resolve": true, + "relative": true + }, + { + "field": "libraries", + "resolve": false, + "relative": false + }, + { + "field": "libpath", + "resolve": true, + "relative": false + } + ], + "confs": [ + { + "task": "build", + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/math-base-napi-unary", + "@stdlib/complex-float64", + "@stdlib/complex-reim", + "@stdlib/math-base-special-cabs" + ] + }, + { + "task": "benchmark", + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/complex-float64", + "@stdlib/complex-reim", + "@stdlib/math-base-special-cabs" + ] + }, + { + "task": "examples", + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/complex-float64", + "@stdlib/complex-reim", + "@stdlib/math-base-special-cabs" + ] + } + ] +} + diff --git a/package.json b/package.json index 307fecc..06d3b2b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@stdlib/math-base-special-csignum", "version": "0.0.7", - "description": "Evaluate the signum function of a complex number.", + "description": "Evaluate the signum function of a double-precision complex floating-point number.", "license": "Apache-2.0", "author": { "name": "The Stdlib Authors", @@ -14,6 +14,7 @@ } ], "main": "./lib", + "gypfile": false, "directories": { "benchmark": "./benchmark", "doc": "./docs", @@ -38,15 +39,16 @@ }, "dependencies": { "@stdlib/complex-float64": "^0.0.8", + "@stdlib/complex-imag": "^0.0.7", + "@stdlib/complex-real": "^0.0.7", + "@stdlib/complex-reim": "^0.0.6", + "@stdlib/math-base-napi-unary": "^0.0.9", "@stdlib/math-base-special-cabs": "^0.0.7", - "@stdlib/types": "^0.0.14" + "@stdlib/types": "^0.0.14", + "@stdlib/utils-library-manifest": "^0.0.8" }, "devDependencies": { - "@stdlib/array-float64": "^0.0.6", - "@stdlib/assert-is-array": "^0.0.7", "@stdlib/bench": "^0.0.12", - "@stdlib/complex-imag": "^0.0.7", - "@stdlib/complex-real": "^0.0.7", "@stdlib/constants-float64-eps": "^0.0.8", "@stdlib/constants-float64-ninf": "^0.0.8", "@stdlib/constants-float64-pinf": "^0.0.8", @@ -54,7 +56,8 @@ "@stdlib/math-base-assert-is-negative-zero": "^0.0.8", "@stdlib/math-base-assert-is-positive-zero": "^0.0.8", "@stdlib/math-base-special-abs": "^0.0.6", - "@stdlib/random-base-randu": "^0.0.8", + "@stdlib/random-base-uniform": "^0.0.6", + "@stdlib/utils-try-require": "^0.0.7", "tape": "git+https://github.com/kgryte/tape.git#fix/globby", "istanbul": "^0.4.1", "tap-min": "git+https://github.com/Planeshifter/tap-min.git" diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..a28d898 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,70 @@ +#/ +# @license Apache-2.0 +# +# Copyright (c) 2021 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + +# VARIABLES # + +ifndef VERBOSE + QUIET := @ +else + QUIET := +endif + +# Determine the OS ([1][1], [2][2]). +# +# [1]: https://en.wikipedia.org/wiki/Uname#Examples +# [2]: http://stackoverflow.com/a/27776822/2225624 +OS ?= $(shell uname) +ifneq (, $(findstring MINGW,$(OS))) + OS := WINNT +else +ifneq (, $(findstring MSYS,$(OS))) + OS := WINNT +else +ifneq (, $(findstring CYGWIN,$(OS))) + OS := WINNT +else +ifneq (, $(findstring Windows_NT,$(OS))) + OS := WINNT +endif +endif +endif +endif + + +# RULES # + +#/ +# Removes generated files for building an add-on. +# +# @example +# make clean-addon +#/ +clean-addon: + $(QUIET) -rm -f *.o *.node + +.PHONY: clean-addon + +#/ +# Removes generated files. +# +# @example +# make clean +#/ +clean: clean-addon + +.PHONY: clean diff --git a/src/addon.c b/src/addon.c new file mode 100644 index 0000000..3784c85 --- /dev/null +++ b/src/addon.c @@ -0,0 +1,23 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/math/base/special/csignum.h" +#include "stdlib/math/base/napi/unary.h" + +// cppcheck-suppress shadowFunction +STDLIB_MATH_BASE_NAPI_MODULE_Z_Z( stdlib_base_csignum ) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d65dcae --- /dev/null +++ b/src/main.c @@ -0,0 +1,58 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/math/base/special/csignum.h" +#include "stdlib/math/base/special/cabs.h" +#include "stdlib/complex/float64.h" +#include "stdlib/complex/reim.h" + +/** +* Evaluates the signum function of a double-precision floating-point complex number. +* +* @param z input value +* @return result +* +* @example +* #include "stdlib/complex/float64.h" +* #include "stdlib/complex/real.h" +* #include "stdlib/complex/imag.h" +* +* stdlib_complex128_t z = stdlib_complex128( -4.2, 5.5 ); +* +* stdlib_complex128_t out = stdlib_base_csignum( z ); +* +* double re = stdlib_real( out ); +* // returns -0.6069136033622302 +* +* double im = stdlib_imag( out ); +* // returns 0.79476781392673 +*/ +stdlib_complex128_t stdlib_base_csignum( const stdlib_complex128_t z ) { + double re; + double im; + double az; + + az = stdlib_base_cabs( z ); + if ( az == 0.0 ) { + return z; + } + stdlib_reim( z, &re, &im ); + re = re / az; + im = im / az; + return stdlib_complex128( re, im ); +} diff --git a/test/test.js b/test/test.js index 9c5b69c..4ad6b4d 100644 --- a/test/test.js +++ b/test/test.js @@ -28,7 +28,9 @@ var EPS = require( '@stdlib/constants-float64-eps' ); var abs = require( '@stdlib/math-base-special-abs' ); var isNegativeZero = require( '@stdlib/math-base-assert-is-negative-zero' ); var isPositiveZero = require( '@stdlib/math-base-assert-is-positive-zero' ); -var Float64Array = require( '@stdlib/array-float64' ); +var Complex128 = require( '@stdlib/complex-float64' ); +var real = require( '@stdlib/complex-real' ); +var imag = require( '@stdlib/complex-imag' ); var csignum = require( './../lib' ); @@ -61,108 +63,54 @@ tape( 'the function evaluates the signum function', function test( t ) { eim = data.expected_im; for ( i = 0; i < re.length; i++ ) { - y = csignum( re[ i ], im[ i ] ); - if ( y[ 0 ] === ere[ i ] && y[ 1 ] === eim[ i ] ) { - t.equal( y[ 0 ], ere[ i ], 're: '+re[ i ]+'. Expected: '+ere[ i ] ); - t.equal( y[ 1 ], eim[ i ], 'im: '+im[ i ]+'. Expected: '+eim[ i ] ); + y = csignum( new Complex128( re[ i ], im[ i ] ) ); + if ( real( y ) === ere[ i ] && imag( y ) === eim[ i ] ) { + t.equal( real( y ), ere[ i ], 're: '+re[ i ]+'. Expected: '+ere[ i ] ); + t.equal( imag( y ), eim[ i ], 'im: '+im[ i ]+'. Expected: '+eim[ i ] ); } else { - delta = abs( y[ 0 ] - ere[ i ] ); + delta = abs( real( y ) - ere[ i ] ); tol = EPS * abs( ere[ i ] ); - t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual re: '+y[ 0 ]+'. Expected re: '+ere[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); + t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual re: '+real( y )+'. Expected re: '+ere[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); - delta = abs( y[ 1 ] - eim[ i ] ); + delta = abs( imag( y ) - eim[ i ] ); tol = EPS * abs( eim[ i ] ); - t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual im: '+y[ 1 ]+'. Expected im: '+eim[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); + t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual im: '+imag( y )+'. Expected im: '+eim[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); } } t.end(); }); -tape( 'the function evaluates the signum function (output array)', function test( t ) { - var expected; - var actual; - var out; - - // Tested against Julia... - out = new Array( 2 ); - actual = csignum( out, -4.2, 5.5 ); - - expected = [ -0.6069136033622302, 0.79476781392673 ]; - - t.deepEqual( actual, expected, 'returns expected value' ); - t.strictEqual( actual, out, 'returns output value' ); - - t.end(); -}); - -tape( 'the function evaluates the signum function (output typed array)', function test( t ) { - var expected; - var actual; - var out; - - // Tested against Julia... - out = new Float64Array( 2 ); - actual = csignum( out, 9.99999, 0.1 ); - - expected = new Float64Array( [ 0.9999500036497025, 0.009999510036007062 ] ); - - t.deepEqual( actual, expected, 'returns expected value' ); - t.strictEqual( actual, out, 'returns output value' ); - - t.end(); -}); - -tape( 'the function evaluates the signum function (output object)', function test( t ) { - var expected; - var actual; - var out; - - // Tested against Julia... - out = {}; - actual = csignum( out, 4.2, -5.5 ); - - expected = { - '0': 0.6069136033622302, - '1': -0.79476781392673 - }; - - t.deepEqual( actual, expected, 'returns expected value' ); - t.strictEqual( actual, out, 'returns output value' ); - - t.end(); -}); - tape( 'the function returns a `NaN` if provided a `NaN`', function test( t ) { - var val = csignum( NaN, NaN ); - t.strictEqual( isnan( val[ 0 ] ), true, 'returns expected value' ); - t.strictEqual( isnan( val[ 1 ] ), true, 'returns expected value' ); + var val = csignum( new Complex128( NaN, NaN ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); t.end(); }); tape( 'the function returns `+0` if provided `+0`', function test( t ) { - var val = csignum( +0.0, +0.0 ); - t.strictEqual( isPositiveZero( val[ 0 ] ), true, 'returns expected value' ); - t.strictEqual( isPositiveZero( val[ 1 ] ), true, 'returns expected value' ); + var val = csignum( new Complex128( +0.0, +0.0 ) ); + t.strictEqual( isPositiveZero( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isPositiveZero( imag( val ) ), true, 'returns expected value' ); t.end(); }); tape( 'the function returns `-0` if provided `-0`', function test( t ) { - var val = csignum( -0.0, -0.0 ); - t.strictEqual( isNegativeZero( val[ 0 ] ), true, 'returns expected value' ); - t.strictEqual( isNegativeZero( val[ 1 ] ), true, 'returns expected value' ); + var val = csignum( new Complex128( -0.0, -0.0 ) ); + t.strictEqual( isNegativeZero( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isNegativeZero( imag( val ) ), true, 'returns expected value' ); t.end(); }); tape( 'the function returns a `NaN` if provided `infinity`', function test( t ) { - var val = csignum( PINF, PINF ); - t.strictEqual( isnan( val[ 0 ] ), true, 'returns expected value' ); - t.strictEqual( isnan( val[ 1 ] ), true, 'returns expected value' ); + var val = csignum( new Complex128( PINF, PINF ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); t.end(); }); tape( 'the function returns a `NaN` if provided `-infinity`', function test( t ) { - var val = csignum( NINF, NINF ); - t.strictEqual( isnan( val[ 0 ] ), true, 'returns expected value' ); - t.strictEqual( isnan( val[ 1 ] ), true, 'returns expected value' ); + var val = csignum( new Complex128( NINF, NINF ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); t.end(); }); diff --git a/test/test.native.js b/test/test.native.js new file mode 100644 index 0000000..1aa9e29 --- /dev/null +++ b/test/test.native.js @@ -0,0 +1,125 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2023 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var tape = require( 'tape' ); +var PINF = require( '@stdlib/constants-float64-pinf' ); +var NINF = require( '@stdlib/constants-float64-ninf' ); +var isnan = require( '@stdlib/math-base-assert-is-nan' ); +var EPS = require( '@stdlib/constants-float64-eps' ); +var abs = require( '@stdlib/math-base-special-abs' ); +var isNegativeZero = require( '@stdlib/math-base-assert-is-negative-zero' ); +var isPositiveZero = require( '@stdlib/math-base-assert-is-positive-zero' ); +var Complex128 = require( '@stdlib/complex-float64' ); +var real = require( '@stdlib/complex-real' ); +var imag = require( '@stdlib/complex-imag' ); +var tryRequire = require( '@stdlib/utils-try-require' ); + + +// VARIABLES // + +var csignum = tryRequire( resolve( __dirname, './../lib/native.js' ) ); +var opts = { + 'skip': ( csignum instanceof Error ) +}; + + +// FIXTURES // + +var data = require( './fixtures/julia/data.json' ); + + +// TESTS // + +tape( 'main export is a function', opts, function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof csignum, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function evaluates the signum function', opts, function test( t ) { + var delta; + var ere; + var eim; + var tol; + var re; + var im; + var y; + var i; + + re = data.re; + im = data.im; + ere = data.expected_re; + eim = data.expected_im; + + for ( i = 0; i < re.length; i++ ) { + y = csignum( new Complex128( re[ i ], im[ i ] ) ); + if ( real( y ) === ere[ i ] && imag( y ) === eim[ i ] ) { + t.equal( real( y ), ere[ i ], 're: '+re[ i ]+'. Expected: '+ere[ i ] ); + t.equal( imag( y ), eim[ i ], 'im: '+im[ i ]+'. Expected: '+eim[ i ] ); + } else { + delta = abs( real( y ) - ere[ i ] ); + tol = EPS * abs( ere[ i ] ); + t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual re: '+real( y )+'. Expected re: '+ere[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); + + delta = abs( imag( y ) - eim[ i ] ); + tol = EPS * abs( eim[ i ] ); + t.ok( delta <= tol, 'within tolerance. re: '+re[ i ]+'. im: '+im[ i ]+'. Actual im: '+imag( y )+'. Expected im: '+eim[ i ]+'. delta: '+delta+'. tol: '+tol+'.' ); + } + } + t.end(); +}); + +tape( 'the function returns a `NaN` if provided a `NaN`', opts, function test( t ) { + var val = csignum( new Complex128( NaN, NaN ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns `+0` if provided `+0`', opts, function test( t ) { + var val = csignum( new Complex128( +0.0, +0.0 ) ); + t.strictEqual( isPositiveZero( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isPositiveZero( imag( val ) ), true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns `-0` if provided `-0`', opts, function test( t ) { + var val = csignum( new Complex128( -0.0, -0.0 ) ); + t.strictEqual( isNegativeZero( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isNegativeZero( imag( val ) ), true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns a `NaN` if provided `infinity`', opts, function test( t ) { + var val = csignum( new Complex128( PINF, PINF ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns a `NaN` if provided `-infinity`', opts, function test( t ) { + var val = csignum( new Complex128( NINF, NINF ) ); + t.strictEqual( isnan( real( val ) ), true, 'returns expected value' ); + t.strictEqual( isnan( imag( val ) ), true, 'returns expected value' ); + t.end(); +});