diff options
author | Pedro Alvarez <pedro.alvarez@codethink.co.uk> | 2016-05-31 10:43:16 +0100 |
---|---|---|
committer | Pedro Alvarez <pedro.alvarez@codethink.co.uk> | 2016-05-31 10:43:16 +0100 |
commit | 7ccad68acf3805b35721e861fb9bc5a9e40effc8 (patch) | |
tree | 311dd8abfb3cf6f42a07f98baeac5f0e950af675 /mpc/src | |
parent | d2a230d1027944e2244aba9a2efe1e3d8b7bc5fc (diff) | |
download | gcc-tarball-7ccad68acf3805b35721e861fb9bc5a9e40effc8.tar.gz |
Import http://www.multiprecision.org/mpc/download/mpc-1.0.3.tar.gzbaserock/pedroalvarez/gcc-5.3.0-v2baserock/pedroalvarez/gcc-5.3.0baserock/gcc-5.3.0
Diffstat (limited to 'mpc/src')
84 files changed, 9603 insertions, 0 deletions
diff --git a/mpc/src/Makefile.am b/mpc/src/Makefile.am new file mode 100644 index 0000000000..0ece3117e8 --- /dev/null +++ b/mpc/src/Makefile.am @@ -0,0 +1,34 @@ +## src/Makefile.am -- Process this file with automake to produce Makefile.in +## +## Copyright (C) 2008, 2009, 2010, 2011, 2012 INRIA +## +## This file is part of GNU MPC. +## +## GNU MPC is free software; you can redistribute it and/or modify it under +## the terms of the GNU Lesser General Public License as published by the +## Free Software Foundation; either version 3 of the License, or (at your +## option) any later version. +## +## GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +## more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this program. If not, see http://www.gnu.org/licenses/ . + +lib_LTLIBRARIES = libmpc.la +libmpc_la_LDFLAGS = $(MPC_LDFLAGS) -version-info 3:0:0 +libmpc_la_SOURCES = mpc-impl.h abs.c acos.c acosh.c add.c add_fr.c \ + add_si.c add_ui.c arg.c asin.c asinh.c atan.c atanh.c clear.c cmp.c \ + cmp_si_si.c conj.c cos.c cosh.c div_2si.c div_2ui.c div.c div_fr.c \ + div_ui.c exp.c fma.c fr_div.c fr_sub.c get_prec2.c get_prec.c \ + get_version.c get_x.c imag.c init2.c init3.c inp_str.c log.c log10.c \ + mem.c mul_2si.c mul_2ui.c mul.c mul_fr.c mul_i.c mul_si.c mul_ui.c \ + neg.c norm.c out_str.c pow.c pow_fr.c \ + pow_ld.c pow_d.c pow_si.c pow_ui.c pow_z.c proj.c real.c urandom.c set.c \ + set_prec.c set_str.c set_x.c set_x_x.c sin.c sin_cos.c sinh.c sqr.c \ + sqrt.c strtoc.c sub.c sub_fr.c sub_ui.c swap.c tan.c tanh.c uceil_log2.c \ + ui_div.c ui_ui_sub.c + +libmpc_la_LIBADD = @LTLIBOBJS@ diff --git a/mpc/src/Makefile.in b/mpc/src/Makefile.in new file mode 100644 index 0000000000..d0fd25de8c --- /dev/null +++ b/mpc/src/Makefile.in @@ -0,0 +1,755 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c_check_flag.m4 \ + $(top_srcdir)/m4/ax_gcc_option.m4 \ + $(top_srcdir)/m4/ax_gcc_version.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/mpc.m4 $(top_srcdir)/m4/valgrind-tests.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libmpc_la_DEPENDENCIES = @LTLIBOBJS@ +am_libmpc_la_OBJECTS = abs.lo acos.lo acosh.lo add.lo add_fr.lo \ + add_si.lo add_ui.lo arg.lo asin.lo asinh.lo atan.lo atanh.lo \ + clear.lo cmp.lo cmp_si_si.lo conj.lo cos.lo cosh.lo div_2si.lo \ + div_2ui.lo div.lo div_fr.lo div_ui.lo exp.lo fma.lo fr_div.lo \ + fr_sub.lo get_prec2.lo get_prec.lo get_version.lo get_x.lo \ + imag.lo init2.lo init3.lo inp_str.lo log.lo log10.lo mem.lo \ + mul_2si.lo mul_2ui.lo mul.lo mul_fr.lo mul_i.lo mul_si.lo \ + mul_ui.lo neg.lo norm.lo out_str.lo pow.lo pow_fr.lo pow_ld.lo \ + pow_d.lo pow_si.lo pow_ui.lo pow_z.lo proj.lo real.lo \ + urandom.lo set.lo set_prec.lo set_str.lo set_x.lo set_x_x.lo \ + sin.lo sin_cos.lo sinh.lo sqr.lo sqrt.lo strtoc.lo sub.lo \ + sub_fr.lo sub_ui.lo swap.lo tan.lo tanh.lo uceil_log2.lo \ + ui_div.lo ui_ui_sub.lo +libmpc_la_OBJECTS = $(am_libmpc_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libmpc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libmpc_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libmpc_la_SOURCES) +DIST_SOURCES = $(libmpc_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + logging.c +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GCC_VERSION = @GCC_VERSION@ +GITVERSION = @GITVERSION@ +GREP = @GREP@ +HASGIT = @HASGIT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPC_LDFLAGS = @MPC_LDFLAGS@ +MPC_LOG_H = @MPC_LOG_H@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +lib_LTLIBRARIES = libmpc.la +libmpc_la_LDFLAGS = $(MPC_LDFLAGS) -version-info 3:0:0 +libmpc_la_SOURCES = mpc-impl.h abs.c acos.c acosh.c add.c add_fr.c \ + add_si.c add_ui.c arg.c asin.c asinh.c atan.c atanh.c clear.c cmp.c \ + cmp_si_si.c conj.c cos.c cosh.c div_2si.c div_2ui.c div.c div_fr.c \ + div_ui.c exp.c fma.c fr_div.c fr_sub.c get_prec2.c get_prec.c \ + get_version.c get_x.c imag.c init2.c init3.c inp_str.c log.c log10.c \ + mem.c mul_2si.c mul_2ui.c mul.c mul_fr.c mul_i.c mul_si.c mul_ui.c \ + neg.c norm.c out_str.c pow.c pow_fr.c \ + pow_ld.c pow_d.c pow_si.c pow_ui.c pow_z.c proj.c real.c urandom.c set.c \ + set_prec.c set_str.c set_x.c set_x_x.c sin.c sin_cos.c sinh.c sqr.c \ + sqrt.c strtoc.c sub.c sub_fr.c sub_ui.c swap.c tan.c tanh.c uceil_log2.c \ + ui_div.c ui_ui_sub.c + +libmpc_la_LIBADD = @LTLIBOBJS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libmpc.la: $(libmpc_la_OBJECTS) $(libmpc_la_DEPENDENCIES) $(EXTRA_libmpc_la_DEPENDENCIES) + $(AM_V_CCLD)$(libmpc_la_LINK) -rpath $(libdir) $(libmpc_la_OBJECTS) $(libmpc_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/logging.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/abs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acos.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acosh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add_fr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add_si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add_ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asinh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atanh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clear.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp_si_si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conj.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cos.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cosh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/div.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/div_2si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/div_2ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/div_fr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/div_ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fma.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fr_div.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fr_sub.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_prec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_prec2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_version.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_x.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init3.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inp_str.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log10.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_2si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_2ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_fr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_i.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mul_ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/neg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/norm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/out_str.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_d.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_fr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_ld.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_si.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pow_z.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proj.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/real.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_prec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_str.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_x.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_x_x.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sin_cos.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sinh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sqr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sqrt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtoc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub_fr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub_ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/swap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tanh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uceil_log2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_div.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_ui_sub.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/urandom.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mpc/src/abs.c b/mpc/src/abs.c new file mode 100644 index 0000000000..bf1e5fda09 --- /dev/null +++ b/mpc/src/abs.c @@ -0,0 +1,28 @@ +/* mpc_abs -- Absolute value of a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* the rounding mode is mpfr_rnd_t here since we return an mpfr number */ +int +mpc_abs (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) +{ + return mpfr_hypot (a, mpc_realref(b), mpc_imagref(b), rnd); +} diff --git a/mpc/src/acos.c b/mpc/src/acos.c new file mode 100644 index 0000000000..e7a269149a --- /dev/null +++ b/mpc/src/acos.c @@ -0,0 +1,228 @@ +/* mpc_acos -- arccosine of a complex number. + +Copyright (C) 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + +int +mpc_acos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + int inex_re, inex_im, inex; + mpfr_prec_t p_re, p_im, p; + mpc_t z1; + mpfr_t pi_over_2; + mpfr_exp_t e1, e2; + mpfr_rnd_t rnd_im; + mpc_rnd_t rnd1; + + inex_re = 0; + inex_im = 0; + + /* special values */ + if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) + { + if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? +1 : -1); + mpfr_set_nan (mpc_realref (rop)); + } + else if (mpfr_zero_p (mpc_realref (op))) + { + inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd)); + mpfr_set_nan (mpc_imagref (rop)); + } + else + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + } + + return MPC_INEX (inex_re, 0); + } + + if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + if (mpfr_inf_p (mpc_realref (op))) + { + if (mpfr_inf_p (mpc_imagref (op))) + { + if (mpfr_sgn (mpc_realref (op)) > 0) + { + inex_re = + set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd)); + mpfr_div_2ui (mpc_realref (rop), mpc_realref (rop), 1, GMP_RNDN); + } + else + { + + /* the real part of the result is 3*pi/4 + a = o(pi) error(a) < 1 ulp(a) + b = o(3*a) error(b) < 2 ulp(b) + c = b/4 exact + thus 1 bit is lost */ + mpfr_t x; + mpfr_prec_t prec; + int ok; + mpfr_init (x); + prec = mpfr_get_prec (mpc_realref (rop)); + p = prec; + + do + { + p += mpc_ceil_log2 (p); + mpfr_set_prec (x, p); + mpfr_const_pi (x, GMP_RNDD); + mpfr_mul_ui (x, x, 3, GMP_RNDD); + ok = + mpfr_can_round (x, p - 1, GMP_RNDD, MPC_RND_RE (rnd), + prec+(MPC_RND_RE (rnd) == GMP_RNDN)); + + } while (ok == 0); + inex_re = + mpfr_div_2ui (mpc_realref (rop), x, 2, MPC_RND_RE (rnd)); + mpfr_clear (x); + } + } + else + { + if (mpfr_sgn (mpc_realref (op)) > 0) + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + else + inex_re = mpfr_const_pi (mpc_realref (rop), MPC_RND_RE (rnd)); + } + } + else + inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd)); + + mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? +1 : -1); + + return MPC_INEX (inex_re, 0); + } + + /* pure real argument */ + if (mpfr_zero_p (mpc_imagref (op))) + { + int s_im; + s_im = mpfr_signbit (mpc_imagref (op)); + + if (mpfr_cmp_ui (mpc_realref (op), 1) > 0) + { + if (s_im) + inex_im = mpfr_acosh (mpc_imagref (rop), mpc_realref (op), + MPC_RND_IM (rnd)); + else + inex_im = -mpfr_acosh (mpc_imagref (rop), mpc_realref (op), + INV_RND (MPC_RND_IM (rnd))); + + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + } + else if (mpfr_cmp_si (mpc_realref (op), -1) < 0) + { + mpfr_t minus_op_re; + minus_op_re[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (minus_op_re); + + if (s_im) + inex_im = mpfr_acosh (mpc_imagref (rop), minus_op_re, + MPC_RND_IM (rnd)); + else + inex_im = -mpfr_acosh (mpc_imagref (rop), minus_op_re, + INV_RND (MPC_RND_IM (rnd))); + inex_re = mpfr_const_pi (mpc_realref (rop), MPC_RND_RE (rnd)); + } + else + { + inex_re = mpfr_acos (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + mpfr_set_ui (mpc_imagref (rop), 0, MPC_RND_IM (rnd)); + } + + if (!s_im) + mpc_conj (rop, rop, MPC_RNDNN); + + return MPC_INEX (inex_re, inex_im); + } + + /* pure imaginary argument */ + if (mpfr_zero_p (mpc_realref (op))) + { + inex_re = set_pi_over_2 (mpc_realref (rop), +1, MPC_RND_RE (rnd)); + inex_im = -mpfr_asinh (mpc_imagref (rop), mpc_imagref (op), + INV_RND (MPC_RND_IM (rnd))); + mpc_conj (rop,rop, MPC_RNDNN); + + return MPC_INEX (inex_re, inex_im); + } + + /* regular complex argument: acos(z) = Pi/2 - asin(z) */ + p_re = mpfr_get_prec (mpc_realref(rop)); + p_im = mpfr_get_prec (mpc_imagref(rop)); + p = p_re; + mpc_init3 (z1, p, p_im); /* we round directly the imaginary part to p_im, + with rounding mode opposite to rnd_im */ + rnd_im = MPC_RND_IM(rnd); + /* the imaginary part of asin(z) has the same sign as Im(z), thus if + Im(z) > 0 and rnd_im = RNDZ, we want to round the Im(asin(z)) to -Inf + so that -Im(asin(z)) is rounded to zero */ + if (rnd_im == GMP_RNDZ) + rnd_im = mpfr_sgn (mpc_imagref(op)) > 0 ? GMP_RNDD : GMP_RNDU; + else + rnd_im = rnd_im == GMP_RNDU ? GMP_RNDD + : rnd_im == GMP_RNDD ? GMP_RNDU + : rnd_im; /* both RNDZ and RNDA map to themselves for -asin(z) */ + rnd1 = MPC_RND (GMP_RNDN, rnd_im); + mpfr_init2 (pi_over_2, p); + for (;;) + { + p += mpc_ceil_log2 (p) + 3; + + mpfr_set_prec (mpc_realref(z1), p); + mpfr_set_prec (pi_over_2, p); + + set_pi_over_2 (pi_over_2, +1, GMP_RNDN); + e1 = 1; /* Exp(pi_over_2) */ + inex = mpc_asin (z1, op, rnd1); /* asin(z) */ + MPC_ASSERT (mpfr_sgn (mpc_imagref(z1)) * mpfr_sgn (mpc_imagref(op)) > 0); + inex_im = MPC_INEX_IM(inex); /* inex_im is in {-1, 0, 1} */ + e2 = mpfr_get_exp (mpc_realref(z1)); + mpfr_sub (mpc_realref(z1), pi_over_2, mpc_realref(z1), GMP_RNDN); + if (!mpfr_zero_p (mpc_realref(z1))) + { + /* the error on x=Re(z1) is bounded by 1/2 ulp(x) + 2^(e1-p-1) + + 2^(e2-p-1) */ + e1 = e1 >= e2 ? e1 + 1 : e2 + 1; + /* the error on x is bounded by 1/2 ulp(x) + 2^(e1-p-1) */ + e1 -= mpfr_get_exp (mpc_realref(z1)); + /* the error on x is bounded by 1/2 ulp(x) [1 + 2^e1] */ + e1 = e1 <= 0 ? 0 : e1; + /* the error on x is bounded by 2^e1 * ulp(x) */ + mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN); /* exact */ + inex_im = -inex_im; + if (mpfr_can_round (mpc_realref(z1), p - e1, GMP_RNDN, GMP_RNDZ, + p_re + (MPC_RND_RE(rnd) == GMP_RNDN))) + break; + } + } + inex = mpc_set (rop, z1, rnd); + inex_re = MPC_INEX_RE(inex); + mpc_clear (z1); + mpfr_clear (pi_over_2); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/acosh.c b/mpc/src/acosh.c new file mode 100644 index 0000000000..782f5550c2 --- /dev/null +++ b/mpc/src/acosh.c @@ -0,0 +1,76 @@ +/* mpc_acosh -- inverse hyperbolic cosine of a complex number. + +Copyright (C) 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_acosh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* acosh(z) = + NaN + i*NaN, if z=0+i*NaN + -i*acos(z), if sign(Im(z)) = - + i*acos(z), if sign(Im(z)) = + + http://functions.wolfram.com/ElementaryFunctions/ArcCosh/27/02/03/01/01/ + */ + mpc_t a; + mpfr_t tmp; + int inex; + + if (mpfr_zero_p (mpc_realref (op)) && mpfr_nan_p (mpc_imagref (op))) + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + return 0; + } + + /* Note reversal of precisions due to later multiplication by i or -i */ + mpc_init3 (a, MPC_PREC_IM(rop), MPC_PREC_RE(rop)); + + if (mpfr_signbit (mpc_imagref (op))) + { + inex = mpc_acos (a, op, + MPC_RND (INV_RND (MPC_RND_IM (rnd)), MPC_RND_RE (rnd))); + + /* change a to -i*a, i.e., -y+i*x to x+i*y */ + tmp[0] = mpc_realref (a)[0]; + mpc_realref (a)[0] = mpc_imagref (a)[0]; + mpc_imagref (a)[0] = tmp[0]; + MPFR_CHANGE_SIGN (mpc_imagref (a)); + inex = MPC_INEX (MPC_INEX_IM (inex), -MPC_INEX_RE (inex)); + } + else + { + inex = mpc_acos (a, op, + MPC_RND (MPC_RND_IM (rnd), INV_RND(MPC_RND_RE (rnd)))); + + /* change a to i*a, i.e., y-i*x to x+i*y */ + tmp[0] = mpc_realref (a)[0]; + mpc_realref (a)[0] = mpc_imagref (a)[0]; + mpc_imagref (a)[0] = tmp[0]; + MPFR_CHANGE_SIGN (mpc_realref (a)); + inex = MPC_INEX (-MPC_INEX_IM (inex), MPC_INEX_RE (inex)); + } + + mpc_set (rop, a, rnd); + + mpc_clear (a); + + return inex; +} diff --git a/mpc/src/add.c b/mpc/src/add.c new file mode 100644 index 0000000000..ee9ec19b3c --- /dev/null +++ b/mpc/src/add.c @@ -0,0 +1,33 @@ +/* mpc_add -- Add two complex numbers. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_add (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_add (mpc_realref(a), mpc_realref(b), mpc_realref(c), MPC_RND_RE(rnd)); + inex_im = mpfr_add (mpc_imagref(a), mpc_imagref(b), mpc_imagref(c), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/add_fr.c b/mpc/src/add_fr.c new file mode 100644 index 0000000000..ea7b59560d --- /dev/null +++ b/mpc/src/add_fr.c @@ -0,0 +1,33 @@ +/* mpc_add_fr -- Add a complex number and a floating-point number. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_add_fr (mpc_ptr a, mpc_srcptr b, mpfr_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_add (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/add_si.c b/mpc/src/add_si.c new file mode 100644 index 0000000000..ba14803e76 --- /dev/null +++ b/mpc/src/add_si.c @@ -0,0 +1,32 @@ +/* mpc_add_si -- Add a complex number and a signed long int. + +Copyright (C) 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_add_si (mpc_ptr rop, mpc_srcptr op1, long int op2, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_add_si (mpc_realref (rop), mpc_realref (op1), op2, MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op1), MPC_RND_IM (rnd)); + + return MPC_INEX (inex_re, inex_im); +} diff --git a/mpc/src/add_ui.c b/mpc/src/add_ui.c new file mode 100644 index 0000000000..85f4d13d2d --- /dev/null +++ b/mpc/src/add_ui.c @@ -0,0 +1,33 @@ +/* mpc_add_ui -- Add a complex number and an unsigned long int. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_add_ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_add_ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/arg.c b/mpc/src/arg.c new file mode 100644 index 0000000000..20b5180926 --- /dev/null +++ b/mpc/src/arg.c @@ -0,0 +1,27 @@ +/* mpc_arg -- Get the argument of a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_arg (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) +{ + return mpfr_atan2 (a, mpc_imagref (b), mpc_realref (b), rnd); +} diff --git a/mpc/src/asin.c b/mpc/src/asin.c new file mode 100644 index 0000000000..bd4e3132de --- /dev/null +++ b/mpc/src/asin.c @@ -0,0 +1,226 @@ +/* mpc_asin -- arcsine of a complex number. + +Copyright (C) 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_asin (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + mpfr_prec_t p, p_re, p_im, incr_p = 0; + mpfr_rnd_t rnd_re, rnd_im; + mpc_t z1; + int inex; + + /* special values */ + if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) + { + if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_inf (mpc_imagref (rop), mpfr_signbit (mpc_imagref (op)) ? -1 : +1); + } + else if (mpfr_zero_p (mpc_realref (op))) + { + mpfr_set (mpc_realref (rop), mpc_realref (op), GMP_RNDN); + mpfr_set_nan (mpc_imagref (rop)); + } + else + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + } + + return 0; + } + + if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + int inex_re; + if (mpfr_inf_p (mpc_realref (op))) + { + int inf_im = mpfr_inf_p (mpc_imagref (op)); + + inex_re = set_pi_over_2 (mpc_realref (rop), + (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd)); + mpfr_set_inf (mpc_imagref (rop), (mpfr_signbit (mpc_imagref (op)) ? -1 : 1)); + + if (inf_im) + mpfr_div_2ui (mpc_realref (rop), mpc_realref (rop), 1, GMP_RNDN); + } + else + { + mpfr_set_zero (mpc_realref (rop), (mpfr_signbit (mpc_realref (op)) ? -1 : 1)); + inex_re = 0; + mpfr_set_inf (mpc_imagref (rop), (mpfr_signbit (mpc_imagref (op)) ? -1 : 1)); + } + + return MPC_INEX (inex_re, 0); + } + + /* pure real argument */ + if (mpfr_zero_p (mpc_imagref (op))) + { + int inex_re; + int inex_im; + int s_im; + s_im = mpfr_signbit (mpc_imagref (op)); + + if (mpfr_cmp_ui (mpc_realref (op), 1) > 0) + { + if (s_im) + inex_im = -mpfr_acosh (mpc_imagref (rop), mpc_realref (op), + INV_RND (MPC_RND_IM (rnd))); + else + inex_im = mpfr_acosh (mpc_imagref (rop), mpc_realref (op), + MPC_RND_IM (rnd)); + inex_re = set_pi_over_2 (mpc_realref (rop), + (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd)); + if (s_im) + mpc_conj (rop, rop, MPC_RNDNN); + } + else if (mpfr_cmp_si (mpc_realref (op), -1) < 0) + { + mpfr_t minus_op_re; + minus_op_re[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (minus_op_re); + + if (s_im) + inex_im = -mpfr_acosh (mpc_imagref (rop), minus_op_re, + INV_RND (MPC_RND_IM (rnd))); + else + inex_im = mpfr_acosh (mpc_imagref (rop), minus_op_re, + MPC_RND_IM (rnd)); + inex_re = set_pi_over_2 (mpc_realref (rop), + (mpfr_signbit (mpc_realref (op)) ? -1 : 1), MPC_RND_RE (rnd)); + if (s_im) + mpc_conj (rop, rop, MPC_RNDNN); + } + else + { + inex_im = mpfr_set_ui (mpc_imagref (rop), 0, MPC_RND_IM (rnd)); + if (s_im) + mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN); + inex_re = mpfr_asin (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + } + + return MPC_INEX (inex_re, inex_im); + } + + /* pure imaginary argument */ + if (mpfr_zero_p (mpc_realref (op))) + { + int inex_im; + int s; + s = mpfr_signbit (mpc_realref (op)); + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + if (s) + mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN); + inex_im = mpfr_asinh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); + + return MPC_INEX (0, inex_im); + } + + /* regular complex: asin(z) = -i*log(i*z+sqrt(1-z^2)) */ + p_re = mpfr_get_prec (mpc_realref(rop)); + p_im = mpfr_get_prec (mpc_imagref(rop)); + rnd_re = MPC_RND_RE(rnd); + rnd_im = MPC_RND_IM(rnd); + p = p_re >= p_im ? p_re : p_im; + mpc_init2 (z1, p); + while (1) + { + mpfr_exp_t ex, ey, err; + + p += mpc_ceil_log2 (p) + 3 + incr_p; /* incr_p is zero initially */ + incr_p = p / 2; + mpfr_set_prec (mpc_realref(z1), p); + mpfr_set_prec (mpc_imagref(z1), p); + + /* z1 <- z^2 */ + mpc_sqr (z1, op, MPC_RNDNN); + /* err(x) <= 1/2 ulp(x), err(y) <= 1/2 ulp(y) */ + /* z1 <- 1-z1 */ + ex = mpfr_get_exp (mpc_realref(z1)); + mpfr_ui_sub (mpc_realref(z1), 1, mpc_realref(z1), GMP_RNDN); + mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN); + ex = ex - mpfr_get_exp (mpc_realref(z1)); + ex = (ex <= 0) ? 0 : ex; + /* err(x) <= 2^ex * ulp(x) */ + ex = ex + mpfr_get_exp (mpc_realref(z1)) - p; + /* err(x) <= 2^ex */ + ey = mpfr_get_exp (mpc_imagref(z1)) - p - 1; + /* err(y) <= 2^ey */ + ex = (ex >= ey) ? ex : ey; /* err(x), err(y) <= 2^ex, i.e., the norm + of the error is bounded by |h|<=2^(ex+1/2) */ + /* z1 <- sqrt(z1): if z1 = z + h, then sqrt(z1) = sqrt(z) + h/2/sqrt(t) */ + ey = mpfr_get_exp (mpc_realref(z1)) >= mpfr_get_exp (mpc_imagref(z1)) + ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1)); + /* we have |z1| >= 2^(ey-1) thus 1/|z1| <= 2^(1-ey) */ + mpc_sqrt (z1, z1, MPC_RNDNN); + ex = (2 * ex + 1) - 2 - (ey - 1); /* |h^2/4/|t| <= 2^ex */ + ex = (ex + 1) / 2; /* ceil(ex/2) */ + /* express ex in terms of ulp(z1) */ + ey = mpfr_get_exp (mpc_realref(z1)) <= mpfr_get_exp (mpc_imagref(z1)) + ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1)); + ex = ex - ey + p; + /* take into account the rounding error in the mpc_sqrt call */ + err = (ex <= 0) ? 1 : ex + 1; + /* err(x) <= 2^err * ulp(x), err(y) <= 2^err * ulp(y) */ + /* z1 <- i*z + z1 */ + ex = mpfr_get_exp (mpc_realref(z1)); + ey = mpfr_get_exp (mpc_imagref(z1)); + mpfr_sub (mpc_realref(z1), mpc_realref(z1), mpc_imagref(op), GMP_RNDN); + mpfr_add (mpc_imagref(z1), mpc_imagref(z1), mpc_realref(op), GMP_RNDN); + if (mpfr_cmp_ui (mpc_realref(z1), 0) == 0 || mpfr_cmp_ui (mpc_imagref(z1), 0) == 0) + continue; + ex -= mpfr_get_exp (mpc_realref(z1)); /* cancellation in x */ + ey -= mpfr_get_exp (mpc_imagref(z1)); /* cancellation in y */ + ex = (ex >= ey) ? ex : ey; /* maximum cancellation */ + err += ex; + err = (err <= 0) ? 1 : err + 1; /* rounding error in sub/add */ + /* z1 <- log(z1): if z1 = z + h, then log(z1) = log(z) + h/t with + |t| >= min(|z1|,|z|) */ + ex = mpfr_get_exp (mpc_realref(z1)); + ey = mpfr_get_exp (mpc_imagref(z1)); + ex = (ex >= ey) ? ex : ey; + err += ex - p; /* revert to absolute error <= 2^err */ + mpc_log (z1, z1, GMP_RNDN); + err -= ex - 1; /* 1/|t| <= 1/|z| <= 2^(1-ex) */ + /* express err in terms of ulp(z1) */ + ey = mpfr_get_exp (mpc_realref(z1)) <= mpfr_get_exp (mpc_imagref(z1)) + ? mpfr_get_exp (mpc_realref(z1)) : mpfr_get_exp (mpc_imagref(z1)); + err = err - ey + p; + /* take into account the rounding error in the mpc_log call */ + err = (err <= 0) ? 1 : err + 1; + /* z1 <- -i*z1 */ + mpfr_swap (mpc_realref(z1), mpc_imagref(z1)); + mpfr_neg (mpc_imagref(z1), mpc_imagref(z1), GMP_RNDN); + if (mpfr_can_round (mpc_realref(z1), p - err, GMP_RNDN, GMP_RNDZ, + p_re + (rnd_re == GMP_RNDN)) && + mpfr_can_round (mpc_imagref(z1), p - err, GMP_RNDN, GMP_RNDZ, + p_im + (rnd_im == GMP_RNDN))) + break; + } + + inex = mpc_set (rop, z1, rnd); + mpc_clear (z1); + + return inex; +} diff --git a/mpc/src/asinh.c b/mpc/src/asinh.c new file mode 100644 index 0000000000..2807a8bb31 --- /dev/null +++ b/mpc/src/asinh.c @@ -0,0 +1,55 @@ +/* mpc_asinh -- inverse hyperbolic sine of a complex number. + +Copyright (C) 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_asinh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* asinh(op) = -i*asin(i*op) */ + int inex; + mpc_t z, a; + mpfr_t tmp; + + /* z = i*op */ + mpc_realref (z)[0] = mpc_imagref (op)[0]; + mpc_imagref (z)[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (mpc_realref (z)); + + /* Note reversal of precisions due to later multiplication by -i */ + mpc_init3 (a, MPC_PREC_IM(rop), MPC_PREC_RE(rop)); + + inex = mpc_asin (a, z, + MPC_RND (INV_RND (MPC_RND_IM (rnd)), MPC_RND_RE (rnd))); + + /* if a = asin(i*op) = x+i*y, and we want y-i*x */ + + /* change a to -i*a */ + tmp[0] = mpc_realref (a)[0]; + mpc_realref (a)[0] = mpc_imagref (a)[0]; + mpc_imagref (a)[0] = tmp[0]; + MPFR_CHANGE_SIGN (mpc_imagref (a)); + + mpc_set (rop, a, MPC_RNDNN); /* exact */ + + mpc_clear (a); + + return MPC_INEX (MPC_INEX_IM (inex), -MPC_INEX_RE (inex)); +} diff --git a/mpc/src/atan.c b/mpc/src/atan.c new file mode 100644 index 0000000000..285f79f6cb --- /dev/null +++ b/mpc/src/atan.c @@ -0,0 +1,367 @@ +/* mpc_atan -- arctangent of a complex number. + +Copyright (C) 2009, 2010, 2011, 2012, 2013 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> +#include "mpc-impl.h" + +/* set rop to + -pi/2 if s < 0 + +pi/2 else + rounded in the direction rnd +*/ +int +set_pi_over_2 (mpfr_ptr rop, int s, mpfr_rnd_t rnd) +{ + int inex; + + inex = mpfr_const_pi (rop, s < 0 ? INV_RND (rnd) : rnd); + mpfr_div_2ui (rop, rop, 1, GMP_RNDN); + if (s < 0) + { + inex = -inex; + mpfr_neg (rop, rop, GMP_RNDN); + } + + return inex; +} + +int +mpc_atan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + int s_re; + int s_im; + int inex_re; + int inex_im; + int inex; + + inex_re = 0; + inex_im = 0; + s_re = mpfr_signbit (mpc_realref (op)); + s_im = mpfr_signbit (mpc_imagref (op)); + + /* special values */ + if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) + { + if (mpfr_nan_p (mpc_realref (op))) + { + mpfr_set_nan (mpc_realref (rop)); + if (mpfr_zero_p (mpc_imagref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); + if (s_im) + mpc_conj (rop, rop, MPC_RNDNN); + } + else + mpfr_set_nan (mpc_imagref (rop)); + } + else + { + if (mpfr_inf_p (mpc_realref (op))) + { + inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); + mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); + } + else + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + } + } + return MPC_INEX (inex_re, 0); + } + + if (mpfr_inf_p (mpc_realref (op)) || mpfr_inf_p (mpc_imagref (op))) + { + inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); + + mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); + if (s_im) + mpc_conj (rop, rop, GMP_RNDN); + + return MPC_INEX (inex_re, 0); + } + + /* pure real argument */ + if (mpfr_zero_p (mpc_imagref (op))) + { + inex_re = mpfr_atan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + + mpfr_set_ui (mpc_imagref (rop), 0, GMP_RNDN); + if (s_im) + mpc_conj (rop, rop, GMP_RNDN); + + return MPC_INEX (inex_re, 0); + } + + /* pure imaginary argument */ + if (mpfr_zero_p (mpc_realref (op))) + { + int cmp_1; + + if (s_im) + cmp_1 = -mpfr_cmp_si (mpc_imagref (op), -1); + else + cmp_1 = mpfr_cmp_ui (mpc_imagref (op), +1); + + if (cmp_1 < 0) + { + /* atan(+0+iy) = +0 +i*atanh(y), if |y| < 1 + atan(-0+iy) = -0 +i*atanh(y), if |y| < 1 */ + + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + if (s_re) + mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN); + + inex_im = mpfr_atanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); + } + else if (cmp_1 == 0) + { + /* atan(+/-0 +i) = +/-0 +i*inf + atan(+/-0 -i) = +/-0 -i*inf */ + mpfr_set_zero (mpc_realref (rop), s_re ? -1 : +1); + mpfr_set_inf (mpc_imagref (rop), s_im ? -1 : +1); + } + else + { + /* atan(+0+iy) = +pi/2 +i*atanh(1/y), if |y| > 1 + atan(-0+iy) = -pi/2 +i*atanh(1/y), if |y| > 1 */ + mpfr_rnd_t rnd_im, rnd_away; + mpfr_t y; + mpfr_prec_t p, p_im; + int ok; + + rnd_im = MPC_RND_IM (rnd); + mpfr_init (y); + p_im = mpfr_get_prec (mpc_imagref (rop)); + p = p_im; + + /* a = o(1/y) with error(a) < 1 ulp(a) + b = o(atanh(a)) with error(b) < (1+2^{1+Exp(a)-Exp(b)}) ulp(b) + + As |atanh (1/y)| > |1/y| we have Exp(a)-Exp(b) <=0 so, at most, + 2 bits of precision are lost. + + We round atanh(1/y) away from 0. + */ + do + { + p += mpc_ceil_log2 (p) + 2; + mpfr_set_prec (y, p); + rnd_away = s_im == 0 ? GMP_RNDU : GMP_RNDD; + inex_im = mpfr_ui_div (y, 1, mpc_imagref (op), rnd_away); + /* FIXME: should we consider the case with unreasonably huge + precision prec(y)>3*exp_min, where atanh(1/Im(op)) could be + representable while 1/Im(op) underflows ? + This corresponds to |y| = 0.5*2^emin, in which case the + result may be wrong. */ + + /* atanh cannot underflow: |atanh(x)| > |x| for |x| < 1 */ + inex_im |= mpfr_atanh (y, y, rnd_away); + + ok = inex_im == 0 + || mpfr_can_round (y, p - 2, rnd_away, GMP_RNDZ, + p_im + (rnd_im == GMP_RNDN)); + } while (ok == 0); + + inex_re = set_pi_over_2 (mpc_realref (rop), -s_re, MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (rop), y, rnd_im); + mpfr_clear (y); + } + return MPC_INEX (inex_re, inex_im); + } + + /* regular number argument */ + { + mpfr_t a, b, x, y; + mpfr_prec_t prec, p; + mpfr_exp_t err, expo; + int ok = 0; + mpfr_t minus_op_re; + mpfr_exp_t op_re_exp, op_im_exp; + mpfr_rnd_t rnd1, rnd2; + + mpfr_inits2 (MPFR_PREC_MIN, a, b, x, y, (mpfr_ptr) 0); + + /* real part: Re(arctan(x+i*y)) = [arctan2(x,1-y) - arctan2(-x,1+y)]/2 */ + minus_op_re[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (minus_op_re); + op_re_exp = mpfr_get_exp (mpc_realref (op)); + op_im_exp = mpfr_get_exp (mpc_imagref (op)); + + prec = mpfr_get_prec (mpc_realref (rop)); /* result precision */ + + /* a = o(1-y) error(a) < 1 ulp(a) + b = o(atan2(x,a)) error(b) < [1+2^{3+Exp(x)-Exp(a)-Exp(b)}] ulp(b) + = kb ulp(b) + c = o(1+y) error(c) < 1 ulp(c) + d = o(atan2(-x,c)) error(d) < [1+2^{3+Exp(x)-Exp(c)-Exp(d)}] ulp(d) + = kd ulp(d) + e = o(b - d) error(e) < [1 + kb*2^{Exp(b}-Exp(e)} + + kd*2^{Exp(d)-Exp(e)}] ulp(e) + error(e) < [1 + 2^{4+Exp(x)-Exp(a)-Exp(e)} + + 2^{4+Exp(x)-Exp(c)-Exp(e)}] ulp(e) + because |atan(u)| < |u| + < [1 + 2^{5+Exp(x)-min(Exp(a),Exp(c)) + -Exp(e)}] ulp(e) + f = e/2 exact + */ + + /* p: working precision */ + p = (op_im_exp > 0 || prec > SAFE_ABS (mpfr_prec_t, op_im_exp)) ? prec + : (prec - op_im_exp); + rnd1 = mpfr_sgn (mpc_realref (op)) > 0 ? GMP_RNDD : GMP_RNDU; + rnd2 = mpfr_sgn (mpc_realref (op)) < 0 ? GMP_RNDU : GMP_RNDD; + + do + { + p += mpc_ceil_log2 (p) + 2; + mpfr_set_prec (a, p); + mpfr_set_prec (b, p); + mpfr_set_prec (x, p); + + /* x = upper bound for atan (x/(1-y)). Since atan is increasing, we + need an upper bound on x/(1-y), i.e., a lower bound on 1-y for + x positive, and an upper bound on 1-y for x negative */ + mpfr_ui_sub (a, 1, mpc_imagref (op), rnd1); + if (mpfr_sgn (a) == 0) /* y is near 1, thus 1+y is near 2, and + expo will be 1 or 2 below */ + { + MPC_ASSERT (mpfr_cmp_ui (mpc_imagref(op), 1) == 0); + /* check for intermediate underflow */ + err = 2; /* ensures err will be expo below */ + } + else + err = mpfr_get_exp (a); /* err = Exp(a) with the notations above */ + mpfr_atan2 (x, mpc_realref (op), a, GMP_RNDU); + + /* b = lower bound for atan (-x/(1+y)): for x negative, we need a + lower bound on -x/(1+y), i.e., an upper bound on 1+y */ + mpfr_add_ui (a, mpc_imagref(op), 1, rnd2); + /* if a is exactly zero, i.e., Im(op) = -1, then the error on a is 0, + and we can simply ignore the terms involving Exp(a) in the error */ + if (mpfr_sgn (a) == 0) + { + MPC_ASSERT (mpfr_cmp_si (mpc_imagref(op), -1) == 0); + /* check for intermediate underflow */ + expo = err; /* will leave err unchanged below */ + } + else + expo = mpfr_get_exp (a); /* expo = Exp(c) with the notations above */ + mpfr_atan2 (b, minus_op_re, a, GMP_RNDD); + + err = err < expo ? err : expo; /* err = min(Exp(a),Exp(c)) */ + mpfr_sub (x, x, b, GMP_RNDU); + + err = 5 + op_re_exp - err - mpfr_get_exp (x); + /* error is bounded by [1 + 2^err] ulp(e) */ + err = err < 0 ? 1 : err + 1; + + mpfr_div_2ui (x, x, 1, GMP_RNDU); + + /* Note: using RND2=RNDD guarantees that if x is exactly representable + on prec + ... bits, mpfr_can_round will return 0 */ + ok = mpfr_can_round (x, p - err, GMP_RNDU, GMP_RNDD, + prec + (MPC_RND_RE (rnd) == GMP_RNDN)); + } while (ok == 0); + + /* Imaginary part + Im(atan(x+I*y)) = 1/4 * [log(x^2+(1+y)^2) - log (x^2 +(1-y)^2)] */ + prec = mpfr_get_prec (mpc_imagref (rop)); /* result precision */ + + /* a = o(1+y) error(a) < 1 ulp(a) + b = o(a^2) error(b) < 5 ulp(b) + c = o(x^2) error(c) < 1 ulp(c) + d = o(b+c) error(d) < 7 ulp(d) + e = o(log(d)) error(e) < [1 + 7*2^{2-Exp(e)}] ulp(e) = ke ulp(e) + f = o(1-y) error(f) < 1 ulp(f) + g = o(f^2) error(g) < 5 ulp(g) + h = o(c+f) error(h) < 7 ulp(h) + i = o(log(h)) error(i) < [1 + 7*2^{2-Exp(i)}] ulp(i) = ki ulp(i) + j = o(e-i) error(j) < [1 + ke*2^{Exp(e)-Exp(j)} + + ki*2^{Exp(i)-Exp(j)}] ulp(j) + error(j) < [1 + 2^{Exp(e)-Exp(j)} + 2^{Exp(i)-Exp(j)} + + 7*2^{3-Exp(j)}] ulp(j) + < [1 + 2^{max(Exp(e),Exp(i))-Exp(j)+1} + + 7*2^{3-Exp(j)}] ulp(j) + k = j/4 exact + */ + err = 2; + p = prec; /* working precision */ + + do + { + p += mpc_ceil_log2 (p) + err; + mpfr_set_prec (a, p); + mpfr_set_prec (b, p); + mpfr_set_prec (y, p); + + /* a = upper bound for log(x^2 + (1+y)^2) */ + ROUND_AWAY (mpfr_add_ui (a, mpc_imagref (op), 1, MPFR_RNDA), a); + mpfr_sqr (a, a, GMP_RNDU); + mpfr_sqr (y, mpc_realref (op), GMP_RNDU); + mpfr_add (a, a, y, GMP_RNDU); + mpfr_log (a, a, GMP_RNDU); + + /* b = lower bound for log(x^2 + (1-y)^2) */ + mpfr_ui_sub (b, 1, mpc_imagref (op), GMP_RNDZ); /* round to zero */ + mpfr_sqr (b, b, GMP_RNDZ); + /* we could write mpfr_sqr (y, mpc_realref (op), GMP_RNDZ) but it is + more efficient to reuse the value of y (x^2) above and subtract + one ulp */ + mpfr_nextbelow (y); + mpfr_add (b, b, y, GMP_RNDZ); + mpfr_log (b, b, GMP_RNDZ); + + mpfr_sub (y, a, b, GMP_RNDU); + + if (mpfr_zero_p (y)) + /* FIXME: happens when x and y have very different magnitudes; + could be handled more efficiently */ + ok = 0; + else + { + expo = MPC_MAX (mpfr_get_exp (a), mpfr_get_exp (b)); + expo = expo - mpfr_get_exp (y) + 1; + err = 3 - mpfr_get_exp (y); + /* error(j) <= [1 + 2^expo + 7*2^err] ulp(j) */ + if (expo <= err) /* error(j) <= [1 + 2^{err+1}] ulp(j) */ + err = (err < 0) ? 1 : err + 2; + else + err = (expo < 0) ? 1 : expo + 2; + + mpfr_div_2ui (y, y, 2, GMP_RNDN); + MPC_ASSERT (!mpfr_zero_p (y)); + /* FIXME: underflow. Since the main term of the Taylor series + in y=0 is 1/(x^2+1) * y, this means that y is very small + and/or x very large; but then the mpfr_zero_p (y) above + should be true. This needs a proof, or better yet, + special code. */ + + ok = mpfr_can_round (y, p - err, GMP_RNDU, GMP_RNDD, + prec + (MPC_RND_IM (rnd) == GMP_RNDN)); + } + } while (ok == 0); + + inex = mpc_set_fr_fr (rop, x, y, rnd); + + mpfr_clears (a, b, x, y, (mpfr_ptr) 0); + return inex; + } +} diff --git a/mpc/src/atanh.c b/mpc/src/atanh.c new file mode 100644 index 0000000000..e5716cc703 --- /dev/null +++ b/mpc/src/atanh.c @@ -0,0 +1,52 @@ +/* mpc_atanh -- inverse hyperbolic tangent of a complex number. + +Copyright (C) 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_atanh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* atanh(op) = -i*atan(i*op) */ + int inex; + mpfr_t tmp; + mpc_t z, a; + + mpc_realref (z)[0] = mpc_imagref (op)[0]; + mpc_imagref (z)[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (mpc_realref (z)); + + /* Note reversal of precisions due to later multiplication by -i */ + mpc_init3 (a, MPC_PREC_IM(rop), MPC_PREC_RE(rop)); + + inex = mpc_atan (a, z, + MPC_RND (INV_RND (MPC_RND_IM (rnd)), MPC_RND_RE (rnd))); + + /* change a to -i*a, i.e., x+i*y to y-i*x */ + tmp[0] = mpc_realref (a)[0]; + mpc_realref (a)[0] = mpc_imagref (a)[0]; + mpc_imagref (a)[0] = tmp[0]; + MPFR_CHANGE_SIGN (mpc_imagref (a)); + + mpc_set (rop, a, rnd); + + mpc_clear (a); + + return MPC_INEX (MPC_INEX_IM (inex), -MPC_INEX_RE (inex)); +} diff --git a/mpc/src/clear.c b/mpc/src/clear.c new file mode 100644 index 0000000000..f76b5db0f9 --- /dev/null +++ b/mpc/src/clear.c @@ -0,0 +1,28 @@ +/* mpc_clear -- Clear a complex variable. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_clear (mpc_t x) +{ + mpfr_clear (mpc_realref(x)); + mpfr_clear (mpc_imagref(x)); +} diff --git a/mpc/src/cmp.c b/mpc/src/cmp.c new file mode 100644 index 0000000000..ce1871c430 --- /dev/null +++ b/mpc/src/cmp.c @@ -0,0 +1,33 @@ +/* mpc_cmp -- Compare two complex numbers. + +Copyright (C) 2002, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff a = b */ +int +mpc_cmp (mpc_srcptr a, mpc_srcptr b) +{ + int cmp_re, cmp_im; + + cmp_re = mpfr_cmp (mpc_realref(a), mpc_realref(b)); + cmp_im = mpfr_cmp (mpc_imagref(a), mpc_imagref(b)); + + return MPC_INEX(cmp_re, cmp_im); +} diff --git a/mpc/src/cmp_si_si.c b/mpc/src/cmp_si_si.c new file mode 100644 index 0000000000..a50b7585f5 --- /dev/null +++ b/mpc/src/cmp_si_si.c @@ -0,0 +1,34 @@ +/* mpc_cmp_si_si -- Compare a complex number to a number of the form + b+c*i with b and c signed integers. + +Copyright (C) 2005, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff a = b */ +int +mpc_cmp_si_si (mpc_srcptr a, long int b, long int c) +{ + int cmp_re, cmp_im; + + cmp_re = mpfr_cmp_si (mpc_realref(a), b); + cmp_im = mpfr_cmp_si (mpc_imagref(a), c); + + return MPC_INEX(cmp_re, cmp_im); +} diff --git a/mpc/src/conj.c b/mpc/src/conj.c new file mode 100644 index 0000000000..0905b1d358 --- /dev/null +++ b/mpc/src/conj.c @@ -0,0 +1,32 @@ +/* mpc_conj -- Conjugate of a complex number. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_conj (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_set (mpc_realref(a), mpc_realref(b), MPC_RND_RE(rnd)); + inex_im = mpfr_neg (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/cos.c b/mpc/src/cos.c new file mode 100644 index 0000000000..3810f3ec4c --- /dev/null +++ b/mpc/src/cos.c @@ -0,0 +1,27 @@ +/* mpc_cos -- cosine of a complex number. + +Copyright (C) 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_cos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + return MPC_INEX2 (mpc_sin_cos (NULL, rop, op, 0, rnd)); +} diff --git a/mpc/src/cosh.c b/mpc/src/cosh.c new file mode 100644 index 0000000000..0d4aab9e4d --- /dev/null +++ b/mpc/src/cosh.c @@ -0,0 +1,35 @@ +/* mpc_cosh -- hyperbolic cosine of a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_cosh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* cosh(op) = cos(i*op) */ + mpc_t z; + + /* z = i*op without copying significand */ + mpc_realref (z)[0] = mpc_imagref (op)[0]; + mpc_imagref (z)[0] = mpc_realref (op)[0]; + MPFR_CHANGE_SIGN (mpc_realref (z)); + + return mpc_cos (rop, z, rnd); +} diff --git a/mpc/src/div.c b/mpc/src/div.c new file mode 100644 index 0000000000..83584b8b5f --- /dev/null +++ b/mpc/src/div.c @@ -0,0 +1,449 @@ +/* mpc_div -- Divide two complex numbers. + +Copyright (C) 2002, 2003, 2004, 2005, 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* this routine deals with the case where w is zero */ +static int +mpc_div_zero (mpc_ptr a, mpc_srcptr z, mpc_srcptr w, mpc_rnd_t rnd) +/* Assumes w==0, implementation according to C99 G.5.1.8 */ +{ + int sign = MPFR_SIGNBIT (mpc_realref (w)); + mpfr_t infty; + + mpfr_init2 (infty, MPFR_PREC_MIN); + mpfr_set_inf (infty, sign); + mpfr_mul (mpc_realref (a), infty, mpc_realref (z), MPC_RND_RE (rnd)); + mpfr_mul (mpc_imagref (a), infty, mpc_imagref (z), MPC_RND_IM (rnd)); + mpfr_clear (infty); + return MPC_INEX (0, 0); /* exact */ +} + +/* this routine deals with the case where z is infinite and w finite */ +static int +mpc_div_inf_fin (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w) +/* Assumes w finite and non-zero and z infinite; implementation + according to C99 G.5.1.8 */ +{ + int a, b, x, y; + + a = (mpfr_inf_p (mpc_realref (z)) ? MPFR_SIGNBIT (mpc_realref (z)) : 0); + b = (mpfr_inf_p (mpc_imagref (z)) ? MPFR_SIGNBIT (mpc_imagref (z)) : 0); + + /* a is -1 if Re(z) = -Inf, 1 if Re(z) = +Inf, 0 if Re(z) is finite + b is -1 if Im(z) = -Inf, 1 if Im(z) = +Inf, 0 if Im(z) is finite */ + + /* x = MPC_MPFR_SIGN (a * mpc_realref (w) + b * mpc_imagref (w)) */ + /* y = MPC_MPFR_SIGN (b * mpc_realref (w) - a * mpc_imagref (w)) */ + if (a == 0 || b == 0) { + /* only one of a or b can be zero, since z is infinite */ + x = a * MPC_MPFR_SIGN (mpc_realref (w)) + b * MPC_MPFR_SIGN (mpc_imagref (w)); + y = b * MPC_MPFR_SIGN (mpc_realref (w)) - a * MPC_MPFR_SIGN (mpc_imagref (w)); + } + else { + /* Both parts of z are infinite; x could be determined by sign + considerations and comparisons. Since operations with non-finite + numbers are not considered time-critical, we let mpfr do the work. */ + mpfr_t sign; + + mpfr_init2 (sign, 2); + /* This is enough to determine the sign of sums and differences. */ + + if (a == 1) + if (b == 1) { + mpfr_add (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + x = MPC_MPFR_SIGN (sign); + mpfr_sub (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + y = MPC_MPFR_SIGN (sign); + } + else { /* b == -1 */ + mpfr_sub (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + x = MPC_MPFR_SIGN (sign); + mpfr_add (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + y = -MPC_MPFR_SIGN (sign); + } + else /* a == -1 */ + if (b == 1) { + mpfr_sub (sign, mpc_imagref (w), mpc_realref (w), GMP_RNDN); + x = MPC_MPFR_SIGN (sign); + mpfr_add (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + y = MPC_MPFR_SIGN (sign); + } + else { /* b == -1 */ + mpfr_add (sign, mpc_realref (w), mpc_imagref (w), GMP_RNDN); + x = -MPC_MPFR_SIGN (sign); + mpfr_sub (sign, mpc_imagref (w), mpc_realref (w), GMP_RNDN); + y = MPC_MPFR_SIGN (sign); + } + mpfr_clear (sign); + } + + if (x == 0) + mpfr_set_nan (mpc_realref (rop)); + else + mpfr_set_inf (mpc_realref (rop), x); + if (y == 0) + mpfr_set_nan (mpc_imagref (rop)); + else + mpfr_set_inf (mpc_imagref (rop), y); + + return MPC_INEX (0, 0); /* exact */ +} + + +/* this routine deals with the case where z if finite and w infinite */ +static int +mpc_div_fin_inf (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w) +/* Assumes z finite and w infinite; implementation according to + C99 G.5.1.8 */ +{ + mpfr_t c, d, a, b, x, y, zero; + + mpfr_init2 (c, 2); /* needed to hold a signed zero, +1 or -1 */ + mpfr_init2 (d, 2); + mpfr_init2 (x, 2); + mpfr_init2 (y, 2); + mpfr_init2 (zero, 2); + mpfr_set_ui (zero, 0ul, GMP_RNDN); + mpfr_init2 (a, mpfr_get_prec (mpc_realref (z))); + mpfr_init2 (b, mpfr_get_prec (mpc_imagref (z))); + + mpfr_set_ui (c, (mpfr_inf_p (mpc_realref (w)) ? 1 : 0), GMP_RNDN); + MPFR_COPYSIGN (c, c, mpc_realref (w), GMP_RNDN); + mpfr_set_ui (d, (mpfr_inf_p (mpc_imagref (w)) ? 1 : 0), GMP_RNDN); + MPFR_COPYSIGN (d, d, mpc_imagref (w), GMP_RNDN); + + mpfr_mul (a, mpc_realref (z), c, GMP_RNDN); /* exact */ + mpfr_mul (b, mpc_imagref (z), d, GMP_RNDN); + mpfr_add (x, a, b, GMP_RNDN); + + mpfr_mul (b, mpc_imagref (z), c, GMP_RNDN); + mpfr_mul (a, mpc_realref (z), d, GMP_RNDN); + mpfr_sub (y, b, a, GMP_RNDN); + + MPFR_COPYSIGN (mpc_realref (rop), zero, x, GMP_RNDN); + MPFR_COPYSIGN (mpc_imagref (rop), zero, y, GMP_RNDN); + + mpfr_clear (c); + mpfr_clear (d); + mpfr_clear (x); + mpfr_clear (y); + mpfr_clear (zero); + mpfr_clear (a); + mpfr_clear (b); + + return MPC_INEX (0, 0); /* exact */ +} + + +static int +mpc_div_real (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w, mpc_rnd_t rnd) +/* Assumes z finite and w finite and non-zero, with imaginary part + of w a signed zero. */ +{ + int inex_re, inex_im; + /* save signs of operands in case there are overlaps */ + int zrs = MPFR_SIGNBIT (mpc_realref (z)); + int zis = MPFR_SIGNBIT (mpc_imagref (z)); + int wrs = MPFR_SIGNBIT (mpc_realref (w)); + int wis = MPFR_SIGNBIT (mpc_imagref (w)); + + /* warning: rop may overlap with z,w so treat the imaginary part first */ + inex_im = mpfr_div (mpc_imagref(rop), mpc_imagref(z), mpc_realref(w), MPC_RND_IM(rnd)); + inex_re = mpfr_div (mpc_realref(rop), mpc_realref(z), mpc_realref(w), MPC_RND_RE(rnd)); + + /* correct signs of zeroes if necessary, which does not affect the + inexact flags */ + if (mpfr_zero_p (mpc_realref (rop))) + mpfr_setsign (mpc_realref (rop), mpc_realref (rop), (zrs != wrs && zis != wis), + GMP_RNDN); /* exact */ + if (mpfr_zero_p (mpc_imagref (rop))) + mpfr_setsign (mpc_imagref (rop), mpc_imagref (rop), (zis != wrs && zrs == wis), + GMP_RNDN); + + return MPC_INEX(inex_re, inex_im); +} + + +static int +mpc_div_imag (mpc_ptr rop, mpc_srcptr z, mpc_srcptr w, mpc_rnd_t rnd) +/* Assumes z finite and w finite and non-zero, with real part + of w a signed zero. */ +{ + int inex_re, inex_im; + int overlap = (rop == z) || (rop == w); + int imag_z = mpfr_zero_p (mpc_realref (z)); + mpfr_t wloc; + mpc_t tmprop; + mpc_ptr dest = (overlap) ? tmprop : rop; + /* save signs of operands in case there are overlaps */ + int zrs = MPFR_SIGNBIT (mpc_realref (z)); + int zis = MPFR_SIGNBIT (mpc_imagref (z)); + int wrs = MPFR_SIGNBIT (mpc_realref (w)); + int wis = MPFR_SIGNBIT (mpc_imagref (w)); + + if (overlap) + mpc_init3 (tmprop, MPC_PREC_RE (rop), MPC_PREC_IM (rop)); + + wloc[0] = mpc_imagref(w)[0]; /* copies mpfr struct IM(w) into wloc */ + inex_re = mpfr_div (mpc_realref(dest), mpc_imagref(z), wloc, MPC_RND_RE(rnd)); + mpfr_neg (wloc, wloc, GMP_RNDN); + /* changes the sign only in wloc, not in w; no need to correct later */ + inex_im = mpfr_div (mpc_imagref(dest), mpc_realref(z), wloc, MPC_RND_IM(rnd)); + + if (overlap) { + /* Note: we could use mpc_swap here, but this might cause problems + if rop and tmprop have been allocated using different methods, since + it will swap the significands of rop and tmprop. See + http://lists.gforge.inria.fr/pipermail/mpc-discuss/2009-August/000504.html */ + mpc_set (rop, tmprop, MPC_RNDNN); /* exact */ + mpc_clear (tmprop); + } + + /* correct signs of zeroes if necessary, which does not affect the + inexact flags */ + if (mpfr_zero_p (mpc_realref (rop))) + mpfr_setsign (mpc_realref (rop), mpc_realref (rop), (zrs != wrs && zis != wis), + GMP_RNDN); /* exact */ + if (imag_z) + mpfr_setsign (mpc_imagref (rop), mpc_imagref (rop), (zis != wrs && zrs == wis), + GMP_RNDN); + + return MPC_INEX(inex_re, inex_im); +} + + +int +mpc_div (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + int ok_re = 0, ok_im = 0; + mpc_t res, c_conj; + mpfr_t q; + mpfr_prec_t prec; + int inex, inexact_prod, inexact_norm, inexact_re, inexact_im, loops = 0; + int underflow_norm, overflow_norm, underflow_prod, overflow_prod; + int underflow_re = 0, overflow_re = 0, underflow_im = 0, overflow_im = 0; + mpfr_rnd_t rnd_re = MPC_RND_RE (rnd), rnd_im = MPC_RND_IM (rnd); + int saved_underflow, saved_overflow; + int tmpsgn; + + /* According to the C standard G.3, there are three types of numbers: */ + /* finite (both parts are usual real numbers; contains 0), infinite */ + /* (at least one part is a real infinity) and all others; the latter */ + /* are numbers containing a nan, but no infinity, and could reasonably */ + /* be called nan. */ + /* By G.5.1.4, infinite/finite=infinite; finite/infinite=0; */ + /* all other divisions that are not finite/finite return nan+i*nan. */ + /* Division by 0 could be handled by the following case of division by */ + /* a real; we handle it separately instead. */ + if (mpc_zero_p (c)) + return mpc_div_zero (a, b, c, rnd); + else if (mpc_inf_p (b) && mpc_fin_p (c)) + return mpc_div_inf_fin (a, b, c); + else if (mpc_fin_p (b) && mpc_inf_p (c)) + return mpc_div_fin_inf (a, b, c); + else if (!mpc_fin_p (b) || !mpc_fin_p (c)) { + mpc_set_nan (a); + return MPC_INEX (0, 0); + } + else if (mpfr_zero_p(mpc_imagref(c))) + return mpc_div_real (a, b, c, rnd); + else if (mpfr_zero_p(mpc_realref(c))) + return mpc_div_imag (a, b, c, rnd); + + prec = MPC_MAX_PREC(a); + + mpc_init2 (res, 2); + mpfr_init (q); + + /* create the conjugate of c in c_conj without allocating new memory */ + mpc_realref (c_conj)[0] = mpc_realref (c)[0]; + mpc_imagref (c_conj)[0] = mpc_imagref (c)[0]; + MPFR_CHANGE_SIGN (mpc_imagref (c_conj)); + + /* save the underflow or overflow flags from MPFR */ + saved_underflow = mpfr_underflow_p (); + saved_overflow = mpfr_overflow_p (); + + do { + loops ++; + prec += loops <= 2 ? mpc_ceil_log2 (prec) + 5 : prec / 2; + + mpc_set_prec (res, prec); + mpfr_set_prec (q, prec); + + /* first compute norm(c) */ + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_norm = mpc_norm (q, c, GMP_RNDU); + underflow_norm = mpfr_underflow_p (); + overflow_norm = mpfr_overflow_p (); + if (underflow_norm) + mpfr_set_ui (q, 0ul, GMP_RNDN); + /* to obtain divisions by 0 later on */ + + /* now compute b*conjugate(c) */ + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_prod = mpc_mul (res, b, c_conj, MPC_RNDZZ); + inexact_re = MPC_INEX_RE (inexact_prod); + inexact_im = MPC_INEX_IM (inexact_prod); + underflow_prod = mpfr_underflow_p (); + overflow_prod = mpfr_overflow_p (); + /* unfortunately, does not distinguish between under-/overflow + in real or imaginary parts + hopefully, the side-effects of mpc_mul do indeed raise the + mpfr exceptions */ + if (overflow_prod) { + int isinf = 0; + tmpsgn = mpfr_sgn (mpc_realref(res)); + if (tmpsgn > 0) + { + mpfr_nextabove (mpc_realref(res)); + isinf = mpfr_inf_p (mpc_realref(res)); + mpfr_nextbelow (mpc_realref(res)); + } + else if (tmpsgn < 0) + { + mpfr_nextbelow (mpc_realref(res)); + isinf = mpfr_inf_p (mpc_realref(res)); + mpfr_nextabove (mpc_realref(res)); + } + if (isinf) + { + mpfr_set_inf (mpc_realref(res), tmpsgn); + overflow_re = 1; + } + tmpsgn = mpfr_sgn (mpc_imagref(res)); + isinf = 0; + if (tmpsgn > 0) + { + mpfr_nextabove (mpc_imagref(res)); + isinf = mpfr_inf_p (mpc_imagref(res)); + mpfr_nextbelow (mpc_imagref(res)); + } + else if (tmpsgn < 0) + { + mpfr_nextbelow (mpc_imagref(res)); + isinf = mpfr_inf_p (mpc_imagref(res)); + mpfr_nextabove (mpc_imagref(res)); + } + if (isinf) + { + mpfr_set_inf (mpc_imagref(res), tmpsgn); + overflow_im = 1; + } + mpc_set (a, res, rnd); + goto end; + } + + /* divide the product by the norm */ + if (inexact_norm == 0 && (inexact_re == 0 || inexact_im == 0)) { + /* The division has good chances to be exact in at least one part. */ + /* Since this can cause problems when not rounding to the nearest, */ + /* we use the division code of mpfr, which handles the situation. */ + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_re |= mpfr_div (mpc_realref (res), mpc_realref (res), q, GMP_RNDZ); + underflow_re = mpfr_underflow_p (); + overflow_re = mpfr_overflow_p (); + ok_re = !inexact_re || underflow_re || overflow_re + || mpfr_can_round (mpc_realref (res), prec - 4, GMP_RNDN, + GMP_RNDZ, MPC_PREC_RE(a) + (rnd_re == GMP_RNDN)); + + if (ok_re) /* compute imaginary part */ { + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_im |= mpfr_div (mpc_imagref (res), mpc_imagref (res), q, GMP_RNDZ); + underflow_im = mpfr_underflow_p (); + overflow_im = mpfr_overflow_p (); + ok_im = !inexact_im || underflow_im || overflow_im + || mpfr_can_round (mpc_imagref (res), prec - 4, GMP_RNDN, + GMP_RNDZ, MPC_PREC_IM(a) + (rnd_im == GMP_RNDN)); + } + } + else { + /* The division is inexact, so for efficiency reasons we invert q */ + /* only once and multiply by the inverse. */ + if (mpfr_ui_div (q, 1ul, q, GMP_RNDZ) || inexact_norm) { + /* if 1/q is inexact, the approximations of the real and + imaginary part below will be inexact, unless RE(res) + or IM(res) is zero */ + inexact_re |= ~mpfr_zero_p (mpc_realref (res)); + inexact_im |= ~mpfr_zero_p (mpc_imagref (res)); + } + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_re |= mpfr_mul (mpc_realref (res), mpc_realref (res), q, GMP_RNDZ); + underflow_re = mpfr_underflow_p (); + overflow_re = mpfr_overflow_p (); + ok_re = !inexact_re || underflow_re || overflow_re + || mpfr_can_round (mpc_realref (res), prec - 4, GMP_RNDN, + GMP_RNDZ, MPC_PREC_RE(a) + (rnd_re == GMP_RNDN)); + + if (ok_re) /* compute imaginary part */ { + mpfr_clear_underflow (); + mpfr_clear_overflow (); + inexact_im |= mpfr_mul (mpc_imagref (res), mpc_imagref (res), q, GMP_RNDZ); + underflow_im = mpfr_underflow_p (); + overflow_im = mpfr_overflow_p (); + ok_im = !inexact_im || underflow_im || overflow_im + || mpfr_can_round (mpc_imagref (res), prec - 4, GMP_RNDN, + GMP_RNDZ, MPC_PREC_IM(a) + (rnd_im == GMP_RNDN)); + } + } + } while ((!ok_re || !ok_im) && !underflow_norm && !overflow_norm + && !underflow_prod && !overflow_prod); + + inex = mpc_set (a, res, rnd); + inexact_re = MPC_INEX_RE (inex); + inexact_im = MPC_INEX_IM (inex); + + end: + /* fix values and inexact flags in case of overflow/underflow */ + /* FIXME: heuristic, certainly does not cover all cases */ + if (overflow_re || (underflow_norm && !underflow_prod)) { + mpfr_set_inf (mpc_realref (a), mpfr_sgn (mpc_realref (res))); + inexact_re = mpfr_sgn (mpc_realref (res)); + } + else if (underflow_re || (overflow_norm && !overflow_prod)) { + inexact_re = mpfr_signbit (mpc_realref (res)) ? 1 : -1; + mpfr_set_zero (mpc_realref (a), -inexact_re); + } + if (overflow_im || (underflow_norm && !underflow_prod)) { + mpfr_set_inf (mpc_imagref (a), mpfr_sgn (mpc_imagref (res))); + inexact_im = mpfr_sgn (mpc_imagref (res)); + } + else if (underflow_im || (overflow_norm && !overflow_prod)) { + inexact_im = mpfr_signbit (mpc_imagref (res)) ? 1 : -1; + mpfr_set_zero (mpc_imagref (a), -inexact_im); + } + + mpc_clear (res); + mpfr_clear (q); + + /* restore underflow and overflow flags from MPFR */ + if (saved_underflow) + mpfr_set_underflow (); + if (saved_overflow) + mpfr_set_overflow (); + + return MPC_INEX (inexact_re, inexact_im); +} diff --git a/mpc/src/div_2si.c b/mpc/src/div_2si.c new file mode 100644 index 0000000000..511f2cb52f --- /dev/null +++ b/mpc/src/div_2si.c @@ -0,0 +1,32 @@ +/* mpc_div_2si -- Divide a complex number by 2^e. + +Copyright (C) 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_div_2si (mpc_ptr a, mpc_srcptr b, long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_div_2si (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_div_2si (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/div_2ui.c b/mpc/src/div_2ui.c new file mode 100644 index 0000000000..cd53855cf6 --- /dev/null +++ b/mpc/src/div_2ui.c @@ -0,0 +1,32 @@ +/* mpc_div_2ui -- Divide a complex number by 2^e. + +Copyright (C) 2002, 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_div_2ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_div_2ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_div_2ui (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/div_fr.c b/mpc/src/div_fr.c new file mode 100644 index 0000000000..d5ea24030f --- /dev/null +++ b/mpc/src/div_fr.c @@ -0,0 +1,39 @@ +/* mpc_div_fr -- Divide a complex number by a floating-point number. + +Copyright (C) 2002, 2008, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_div_fr (mpc_ptr a, mpc_srcptr b, mpfr_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + mpfr_t real; + + /* We have to use temporary variable in case c=mpc_realref (a). */ + mpfr_init2 (real, MPC_PREC_RE (a)); + + inex_re = mpfr_div (real, mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_div (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + mpfr_set (mpc_realref (a), real, GMP_RNDN); + + mpfr_clear (real); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/div_ui.c b/mpc/src/div_ui.c new file mode 100644 index 0000000000..26debf7dfe --- /dev/null +++ b/mpc/src/div_ui.c @@ -0,0 +1,32 @@ +/* mpc_div_ui -- Divide a complex number by a nonnegative integer. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_div_ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_div_ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_div_ui (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/exp.c b/mpc/src/exp.c new file mode 100644 index 0000000000..36462251c1 --- /dev/null +++ b/mpc/src/exp.c @@ -0,0 +1,202 @@ +/* mpc_exp -- exponential of a complex number. + +Copyright (C) 2002, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_exp (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + mpfr_t x, y, z; + mpfr_prec_t prec; + int ok = 0; + int inex_re, inex_im; + int saved_underflow, saved_overflow; + + /* special values */ + if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) + /* NaNs + exp(nan +i*y) = nan -i*0 if y = -0, + nan +i*0 if y = +0, + nan +i*nan otherwise + exp(x+i*nan) = +/-0 +/-i*0 if x=-inf, + +/-inf +i*nan if x=+inf, + nan +i*nan otherwise */ + { + if (mpfr_zero_p (mpc_imagref (op))) + return mpc_set (rop, op, MPC_RNDNN); + + if (mpfr_inf_p (mpc_realref (op))) + { + if (mpfr_signbit (mpc_realref (op))) + return mpc_set_ui_ui (rop, 0, 0, MPC_RNDNN); + else + { + mpfr_set_inf (mpc_realref (rop), +1); + mpfr_set_nan (mpc_imagref (rop)); + return MPC_INEX(0, 0); /* Inf/NaN are exact */ + } + } + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + return MPC_INEX(0, 0); /* NaN is exact */ + } + + + if (mpfr_zero_p (mpc_imagref(op))) + /* special case when the input is real + exp(x-i*0) = exp(x) -i*0, even if x is NaN + exp(x+i*0) = exp(x) +i*0, even if x is NaN */ + { + inex_re = mpfr_exp (mpc_realref(rop), mpc_realref(op), MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(rop), mpc_imagref(op), MPC_RND_IM(rnd)); + return MPC_INEX(inex_re, inex_im); + } + + if (mpfr_zero_p (mpc_realref (op))) + /* special case when the input is imaginary */ + { + inex_re = mpfr_cos (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE(rnd)); + inex_im = mpfr_sin (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM(rnd)); + return MPC_INEX(inex_re, inex_im); + } + + + if (mpfr_inf_p (mpc_realref (op))) + /* real part is an infinity, + exp(-inf +i*y) = 0*(cos y +i*sin y) + exp(+inf +i*y) = +/-inf +i*nan if y = +/-inf + +inf*(cos y +i*sin y) if 0 < |y| < inf */ + { + mpfr_t n; + + mpfr_init2 (n, 2); + if (mpfr_signbit (mpc_realref (op))) + mpfr_set_ui (n, 0, GMP_RNDN); + else + mpfr_set_inf (n, +1); + + if (mpfr_inf_p (mpc_imagref (op))) + { + inex_re = mpfr_set (mpc_realref (rop), n, GMP_RNDN); + if (mpfr_signbit (mpc_realref (op))) + inex_im = mpfr_set (mpc_imagref (rop), n, GMP_RNDN); + else + { + mpfr_set_nan (mpc_imagref (rop)); + inex_im = 0; /* NaN is exact */ + } + } + else + { + mpfr_t c, s; + mpfr_init2 (c, 2); + mpfr_init2 (s, 2); + + mpfr_sin_cos (s, c, mpc_imagref (op), GMP_RNDN); + inex_re = mpfr_copysign (mpc_realref (rop), n, c, GMP_RNDN); + inex_im = mpfr_copysign (mpc_imagref (rop), n, s, GMP_RNDN); + + mpfr_clear (s); + mpfr_clear (c); + } + + mpfr_clear (n); + return MPC_INEX(inex_re, inex_im); + } + + if (mpfr_inf_p (mpc_imagref (op))) + /* real part is finite non-zero number, imaginary part is an infinity */ + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + return MPC_INEX(0, 0); /* NaN is exact */ + } + + + /* from now on, both parts of op are regular numbers */ + + prec = MPC_MAX_PREC(rop) + + MPC_MAX (MPC_MAX (-mpfr_get_exp (mpc_realref (op)), 0), + -mpfr_get_exp (mpc_imagref (op))); + /* When op is close to 0, then exp is close to 1+Re(op), while + cos is close to 1-Im(op); to decide on the ternary value of exp*cos, + we need a high enough precision so that none of exp or cos is + computed as 1. */ + mpfr_init2 (x, 2); + mpfr_init2 (y, 2); + mpfr_init2 (z, 2); + + /* save the underflow or overflow flags from MPFR */ + saved_underflow = mpfr_underflow_p (); + saved_overflow = mpfr_overflow_p (); + + do + { + prec += mpc_ceil_log2 (prec) + 5; + + mpfr_set_prec (x, prec); + mpfr_set_prec (y, prec); + mpfr_set_prec (z, prec); + + /* FIXME: x may overflow so x.y does overflow too, while Re(exp(op)) + could be represented in the precision of rop. */ + mpfr_clear_overflow (); + mpfr_clear_underflow (); + mpfr_exp (x, mpc_realref(op), GMP_RNDN); /* error <= 0.5ulp */ + mpfr_sin_cos (z, y, mpc_imagref(op), GMP_RNDN); /* errors <= 0.5ulp */ + mpfr_mul (y, y, x, GMP_RNDN); /* error <= 2ulp */ + ok = mpfr_overflow_p () || mpfr_zero_p (x) + || mpfr_can_round (y, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == GMP_RNDN)); + if (ok) /* compute imaginary part */ + { + mpfr_mul (z, z, x, GMP_RNDN); + ok = mpfr_overflow_p () || mpfr_zero_p (x) + || mpfr_can_round (z, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == GMP_RNDN)); + } + } + while (ok == 0); + + inex_re = mpfr_set (mpc_realref(rop), y, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(rop), z, MPC_RND_IM(rnd)); + if (mpfr_overflow_p ()) { + /* overflow in real exponential, inex is sign of infinite result */ + inex_re = mpfr_sgn (y); + inex_im = mpfr_sgn (z); + } + else if (mpfr_underflow_p ()) { + /* underflow in real exponential, inex is opposite of sign of 0 result */ + inex_re = (mpfr_signbit (y) ? +1 : -1); + inex_im = (mpfr_signbit (z) ? +1 : -1); + } + + mpfr_clear (x); + mpfr_clear (y); + mpfr_clear (z); + + /* restore underflow and overflow flags from MPFR */ + if (saved_underflow) + mpfr_set_underflow (); + if (saved_overflow) + mpfr_set_overflow (); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/fma.c b/mpc/src/fma.c new file mode 100644 index 0000000000..7f5cd31b8a --- /dev/null +++ b/mpc/src/fma.c @@ -0,0 +1,191 @@ +/* mpc_fma -- Fused multiply-add of three complex numbers + +Copyright (C) 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return a bound on the precision needed to add or subtract x and y exactly */ +static mpfr_prec_t +bound_prec_addsub (mpfr_srcptr x, mpfr_srcptr y) +{ + if (!mpfr_regular_p (x)) + return mpfr_get_prec (y); + else if (!mpfr_regular_p (y)) + return mpfr_get_prec (x); + else /* neither x nor y are NaN, Inf or zero */ + { + mpfr_exp_t ex = mpfr_get_exp (x); + mpfr_exp_t ey = mpfr_get_exp (y); + mpfr_exp_t ulpx = ex - mpfr_get_prec (x); + mpfr_exp_t ulpy = ey - mpfr_get_prec (y); + return ((ex >= ey) ? ex : ey) + 1 - ((ulpx <= ulpy) ? ulpx : ulpy); + } +} + +/* r <- a*b+c */ +int +mpc_fma_naive (mpc_ptr r, mpc_srcptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + mpfr_t rea_reb, rea_imb, ima_reb, ima_imb, tmp; + mpfr_prec_t pre12, pre13, pre23, pim12, pim13, pim23; + int inex_re, inex_im; + + mpfr_init2 (rea_reb, mpfr_get_prec (mpc_realref(a)) + mpfr_get_prec (mpc_realref(b))); + mpfr_init2 (rea_imb, mpfr_get_prec (mpc_realref(a)) + mpfr_get_prec (mpc_imagref(b))); + mpfr_init2 (ima_reb, mpfr_get_prec (mpc_imagref(a)) + mpfr_get_prec (mpc_realref(b))); + mpfr_init2 (ima_imb, mpfr_get_prec (mpc_imagref(a)) + mpfr_get_prec (mpc_imagref(b))); + + mpfr_mul (rea_reb, mpc_realref(a), mpc_realref(b), GMP_RNDZ); /* exact */ + mpfr_mul (rea_imb, mpc_realref(a), mpc_imagref(b), GMP_RNDZ); /* exact */ + mpfr_mul (ima_reb, mpc_imagref(a), mpc_realref(b), GMP_RNDZ); /* exact */ + mpfr_mul (ima_imb, mpc_imagref(a), mpc_imagref(b), GMP_RNDZ); /* exact */ + + /* Re(r) <- rea_reb - ima_imb + Re(c) */ + + pre12 = bound_prec_addsub (rea_reb, ima_imb); /* bound on exact precision for + rea_reb - ima_imb */ + pre13 = bound_prec_addsub (rea_reb, mpc_realref(c)); + /* bound for rea_reb + Re(c) */ + pre23 = bound_prec_addsub (ima_imb, mpc_realref(c)); + /* bound for ima_imb - Re(c) */ + if (pre12 <= pre13 && pre12 <= pre23) /* (rea_reb - ima_imb) + Re(c) */ + { + mpfr_init2 (tmp, pre12); + mpfr_sub (tmp, rea_reb, ima_imb, GMP_RNDZ); /* exact */ + inex_re = mpfr_add (mpc_realref(r), tmp, mpc_realref(c), MPC_RND_RE(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the real part of both, it is ok */ + } + else if (pre13 <= pre23) /* (rea_reb + Re(c)) - ima_imb */ + { + mpfr_init2 (tmp, pre13); + mpfr_add (tmp, rea_reb, mpc_realref(c), GMP_RNDZ); /* exact */ + inex_re = mpfr_sub (mpc_realref(r), tmp, ima_imb, MPC_RND_RE(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the real part of both, it is ok */ + } + else /* rea_reb + (Re(c) - ima_imb) */ + { + mpfr_init2 (tmp, pre23); + mpfr_sub (tmp, mpc_realref(c), ima_imb, GMP_RNDZ); /* exact */ + inex_re = mpfr_add (mpc_realref(r), tmp, rea_reb, MPC_RND_RE(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the real part of both, it is ok */ + } + + /* Im(r) <- rea_imb + ima_reb + Im(c) */ + pim12 = bound_prec_addsub (rea_imb, ima_reb); /* bound on exact precision for + rea_imb + ima_reb */ + pim13 = bound_prec_addsub (rea_imb, mpc_imagref(c)); + /* bound for rea_imb + Im(c) */ + pim23 = bound_prec_addsub (ima_reb, mpc_imagref(c)); + /* bound for ima_reb + Im(c) */ + if (pim12 <= pim13 && pim12 <= pim23) /* (rea_imb + ima_reb) + Im(c) */ + { + mpfr_set_prec (tmp, pim12); + mpfr_add (tmp, rea_imb, ima_reb, GMP_RNDZ); /* exact */ + inex_im = mpfr_add (mpc_imagref(r), tmp, mpc_imagref(c), MPC_RND_IM(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the imaginary part of both, it is ok */ + } + else if (pim13 <= pim23) /* (rea_imb + Im(c)) + ima_reb */ + { + mpfr_set_prec (tmp, pim13); + mpfr_add (tmp, rea_imb, mpc_imagref(c), GMP_RNDZ); /* exact */ + inex_im = mpfr_add (mpc_imagref(r), tmp, ima_reb, MPC_RND_IM(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the imaginary part of both, it is ok */ + } + else /* rea_imb + (Im(c) + ima_reb) */ + { + mpfr_set_prec (tmp, pre23); + mpfr_add (tmp, mpc_imagref(c), ima_reb, GMP_RNDZ); /* exact */ + inex_im = mpfr_add (mpc_imagref(r), tmp, rea_imb, MPC_RND_IM(rnd)); + /* the only possible bad overlap is between r and c, but since we are + only touching the imaginary part of both, it is ok */ + } + + mpfr_clear (rea_reb); + mpfr_clear (rea_imb); + mpfr_clear (ima_reb); + mpfr_clear (ima_imb); + mpfr_clear (tmp); + + return MPC_INEX(inex_re, inex_im); +} + +/* The algorithm is as follows: + - in a first pass, we use the target precision + some extra bits + - if it fails, we add the number of cancelled bits when adding + Re(a*b) and Re(c) [similarly for the imaginary part] + - it is fails again, we call the mpc_fma_naive function, which also + deals with the special cases */ +int +mpc_fma (mpc_ptr r, mpc_srcptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + mpc_t ab; + mpfr_prec_t pre, pim, wpre, wpim; + mpfr_exp_t diffre, diffim; + int i, inex = 0, okre = 0, okim = 0; + + if (mpc_fin_p (a) == 0 || mpc_fin_p (b) == 0 || mpc_fin_p (c) == 0) + return mpc_fma_naive (r, a, b, c, rnd); + + pre = mpfr_get_prec (mpc_realref(r)); + pim = mpfr_get_prec (mpc_imagref(r)); + wpre = pre + mpc_ceil_log2 (pre) + 10; + wpim = pim + mpc_ceil_log2 (pim) + 10; + mpc_init3 (ab, wpre, wpim); + for (i = 0; i < 2; ++i) + { + mpc_mul (ab, a, b, MPC_RNDZZ); + if (mpfr_zero_p (mpc_realref(ab)) || mpfr_zero_p (mpc_imagref(ab))) + break; + diffre = mpfr_get_exp (mpc_realref(ab)); + diffim = mpfr_get_exp (mpc_imagref(ab)); + mpc_add (ab, ab, c, MPC_RNDZZ); + if (mpfr_zero_p (mpc_realref(ab)) || mpfr_zero_p (mpc_imagref(ab))) + break; + diffre -= mpfr_get_exp (mpc_realref(ab)); + diffim -= mpfr_get_exp (mpc_imagref(ab)); + diffre = (diffre > 0 ? diffre + 1 : 1); + diffim = (diffim > 0 ? diffim + 1 : 1); + okre = diffre > (mpfr_exp_t) wpre ? 0 : mpfr_can_round (mpc_realref(ab), + wpre - diffre, GMP_RNDN, GMP_RNDZ, + pre + (MPC_RND_RE (rnd) == GMP_RNDN)); + okim = diffim > (mpfr_exp_t) wpim ? 0 : mpfr_can_round (mpc_imagref(ab), + wpim - diffim, GMP_RNDN, GMP_RNDZ, + pim + (MPC_RND_IM (rnd) == GMP_RNDN)); + if (okre && okim) + { + inex = mpc_set (r, ab, rnd); + break; + } + if (i == 1) + break; + if (okre == 0 && diffre > 1) + wpre += diffre; + if (okim == 0 && diffim > 1) + wpim += diffim; + mpfr_set_prec (mpc_realref(ab), wpre); + mpfr_set_prec (mpc_imagref(ab), wpim); + } + mpc_clear (ab); + return okre && okim ? inex : mpc_fma_naive (r, a, b, c, rnd); +} diff --git a/mpc/src/fr_div.c b/mpc/src/fr_div.c new file mode 100644 index 0000000000..e57eced1f8 --- /dev/null +++ b/mpc/src/fr_div.c @@ -0,0 +1,39 @@ +/* mpc_fr_div -- Divide a floating-point number by a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_fr_div (mpc_ptr a, mpfr_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + mpc_t bc; + int inexact; + + mpc_realref (bc)[0] = b [0]; + mpfr_init (mpc_imagref (bc)); + /* we consider the operand b to have imaginary part +0 */ + mpfr_set_ui (mpc_imagref (bc), 0, GMP_RNDN); + + inexact = mpc_div (a, bc, c, rnd); + + mpfr_clear (mpc_imagref (bc)); + + return inexact; +} diff --git a/mpc/src/fr_sub.c b/mpc/src/fr_sub.c new file mode 100644 index 0000000000..91338a4fd6 --- /dev/null +++ b/mpc/src/fr_sub.c @@ -0,0 +1,33 @@ +/* mpc_fr_sub -- Substract a complex number from a floating-point number. + +Copyright (C) 2008, 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_fr_sub (mpc_ptr a, mpfr_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_sub (mpc_realref (a), b, mpc_realref (c), MPC_RND_RE (rnd)); + inex_im = mpfr_neg (mpc_imagref (a), mpc_imagref (c), MPC_RND_IM (rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/get_prec.c b/mpc/src/get_prec.c new file mode 100644 index 0000000000..f1fe856c3e --- /dev/null +++ b/mpc/src/get_prec.c @@ -0,0 +1,28 @@ +/* mpc_get_prec -- returns the common precision of real and imaginary part, or 0 if they differ + +Copyright (C) 2007, 2009, 2010 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +mpfr_prec_t +mpc_get_prec (mpc_srcptr x) +{ + mpfr_prec_t precre = MPC_PREC_RE (x); + return (MPC_PREC_IM (x) == precre ? precre : 0); +} diff --git a/mpc/src/get_prec2.c b/mpc/src/get_prec2.c new file mode 100644 index 0000000000..79015c8392 --- /dev/null +++ b/mpc/src/get_prec2.c @@ -0,0 +1,29 @@ +/* mpc_get_prec2 -- returns the precisions of the real and of the imaginary + part through the first two arguments + +Copyright (C) 2007, 2009, 2010 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_get_prec2 (mpfr_prec_t *pr, mpfr_prec_t *pi, mpc_srcptr x) +{ + *pr = MPC_PREC_RE (x); + *pi = MPC_PREC_IM (x); +} diff --git a/mpc/src/get_version.c b/mpc/src/get_version.c new file mode 100644 index 0000000000..88e7e986a9 --- /dev/null +++ b/mpc/src/get_version.c @@ -0,0 +1,49 @@ +/* mpc_get_version -- MPC version + +Copyright (C) 2008, 2009, 2010, 2011, 2012, 2014, 2015 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +#if MPFR_VERSION_MAJOR < 3 +/* The following are functions defined for compatibility with mpfr < 3; + logically, they should be defined in a separate file, but then gcc + complains about an empty translation unit with mpfr >= 3. */ + +void +mpfr_set_zero (mpfr_ptr z, int s) +{ + mpfr_set_ui (z, 0ul, GMP_RNDN); + if (s < 0) + mpfr_neg (z, z, GMP_RNDN); +} + +int +mpfr_regular_p (mpfr_srcptr z) +{ + return (mpfr_number_p (z) && !mpfr_zero_p (z)); +} +#endif /* mpfr < 3 */ + + +const char * +mpc_get_version (void) +{ + return "1.0.3"; +} + diff --git a/mpc/src/get_x.c b/mpc/src/get_x.c new file mode 100644 index 0000000000..31610ac61d --- /dev/null +++ b/mpc/src/get_x.c @@ -0,0 +1,236 @@ +/* mpc_get_dc, mpc_get_ldc -- Transform mpc number into C complex number + mpc_get_str -- Convert a complex number into a string. + +Copyright (C) 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "config.h" + +#ifdef HAVE_COMPLEX_H +#include <complex.h> +#endif + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#include <stdio.h> /* for sprintf, fprintf */ +#include <ctype.h> +#include <string.h> +#include "mpc-impl.h" + +#ifdef HAVE_COMPLEX_H +double _Complex +mpc_get_dc (mpc_srcptr op, mpc_rnd_t rnd) { + return I * mpfr_get_d (mpc_imagref (op), MPC_RND_IM (rnd)) + + mpfr_get_d (mpc_realref (op), MPC_RND_RE (rnd)); +} + +long double _Complex +mpc_get_ldc (mpc_srcptr op, mpc_rnd_t rnd) { + return I * mpfr_get_ld (mpc_imagref (op), MPC_RND_IM (rnd)) + + mpfr_get_ld (mpc_realref (op), MPC_RND_RE (rnd)); +} +#endif + + +/* Code for mpc_get_str. The output format is "(real imag)", the decimal point + of the locale is used. */ + +/* mpfr_prec_t can be either int or long int */ +#if (__GMP_MP_SIZE_T_INT == 1) +#define MPC_EXP_FORMAT_SPEC "i" +#elif (__GMP_MP_SIZE_T_INT == 0) +#define MPC_EXP_FORMAT_SPEC "li" +#else +#error "mpfr_exp_t size not supported" +#endif + +static char * +pretty_zero (mpfr_srcptr zero) +{ + char *pretty; + + pretty = mpc_alloc_str (3); + + pretty[0] = mpfr_signbit (zero) ? '-' : '+'; + pretty[1] = '0'; + pretty[2] = '\0'; + + return pretty; +} + +static char * +prettify (const char *str, const mp_exp_t expo, int base, int special) +{ + size_t sz; + char *pretty; + char *p; + const char *s; + mp_exp_t x; + int sign; + + sz = strlen (str) + 1; /* + terminal '\0' */ + + if (special) + { + /* special number: nan or inf */ + pretty = mpc_alloc_str (sz); + strcpy (pretty, str); + + return pretty; + } + + /* regular number */ + + sign = (str[0] == '-' || str[0] == '+'); + + x = expo - 1; /* expo is the exponent value with decimal point BEFORE + the first digit, we wants decimal point AFTER the first + digit */ + if (base == 16) + x <<= 2; /* the output exponent is a binary exponent */ + + ++sz; /* + decimal point */ + + if (x != 0) + { + /* augment sz with the size needed for an exponent written in base + ten */ + mp_exp_t xx; + + sz += 3; /* + exponent char + sign + 1 digit */ + + if (x < 0) + { + /* avoid overflow when changing sign (assuming that, for the + mp_exp_t type, (max value) is greater than (- min value / 10)) */ + if (x < -10) + { + xx = - (x / 10); + sz++; + } + else + xx = -x; + } + else + xx = x; + + /* compute sz += floor(log(expo)/log(10)) without using libm + functions */ + while (xx > 9) + { + sz++; + xx /= 10; + } + } + + pretty = mpc_alloc_str (sz); + p = pretty; + + /* 1. optional sign plus first digit */ + s = str; + *p++ = *s++; + if (sign) + *p++ = *s++; + + /* 2. decimal point */ +#ifdef HAVE_LOCALECONV + *p++ = *localeconv ()->decimal_point; +#else + *p++ = '.'; +#endif + *p = '\0'; + + /* 3. other significant digits */ + strcat (pretty, s); + + /* 4. exponent (in base ten) */ + if (x == 0) + return pretty; + + p = pretty + strlen (str) + 1; + + switch (base) + { + case 10: + *p++ = 'e'; + break; + case 2: + case 16: + *p++ = 'p'; + break; + default: + *p++ = '@'; + } + + *p = '\0'; + + sprintf (p, "%+"MPC_EXP_FORMAT_SPEC, x); + + return pretty; +} + +static char * +get_pretty_str (const int base, const size_t n, mpfr_srcptr x, mpfr_rnd_t rnd) +{ + mp_exp_t expo; + char *ugly; + char *pretty; + + if (mpfr_zero_p (x)) + return pretty_zero (x); + + ugly = mpfr_get_str (NULL, &expo, base, n, x, rnd); + MPC_ASSERT (ugly != NULL); + pretty = prettify (ugly, expo, base, !mpfr_number_p (x)); + mpfr_free_str (ugly); + + return pretty; +} + +char * +mpc_get_str (int base, size_t n, mpc_srcptr op, mpc_rnd_t rnd) +{ + size_t needed_size; + char *real_str; + char *imag_str; + char *complex_str = NULL; + + if (base < 2 || base > 36) + return NULL; + + real_str = get_pretty_str (base, n, mpc_realref (op), MPC_RND_RE (rnd)); + imag_str = get_pretty_str (base, n, mpc_imagref (op), MPC_RND_IM (rnd)); + + needed_size = strlen (real_str) + strlen (imag_str) + 4; + + complex_str = mpc_alloc_str (needed_size); +MPC_ASSERT (complex_str != NULL); + + strcpy (complex_str, "("); + strcat (complex_str, real_str); + strcat (complex_str, " "); + strcat (complex_str, imag_str); + strcat (complex_str, ")"); + + mpc_free_str (real_str); + mpc_free_str (imag_str); + + return complex_str; +} diff --git a/mpc/src/imag.c b/mpc/src/imag.c new file mode 100644 index 0000000000..5f3b3a5873 --- /dev/null +++ b/mpc/src/imag.c @@ -0,0 +1,27 @@ +/* mpc_imag -- Get the imaginary part of a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_imag (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) +{ + return mpfr_set (a, mpc_imagref (b), rnd); +} diff --git a/mpc/src/init2.c b/mpc/src/init2.c new file mode 100644 index 0000000000..ce4173e72e --- /dev/null +++ b/mpc/src/init2.c @@ -0,0 +1,28 @@ +/* mpc_init2 -- Initialize a complex variable with a given precision. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_init2 (mpc_t x, mpfr_prec_t prec) +{ + mpfr_init2 (mpc_realref(x), prec); + mpfr_init2 (mpc_imagref(x), prec); +} diff --git a/mpc/src/init3.c b/mpc/src/init3.c new file mode 100644 index 0000000000..69f91b2580 --- /dev/null +++ b/mpc/src/init3.c @@ -0,0 +1,28 @@ +/* mpc_init3 -- Initialize a complex variable with given precisions. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_init3 (mpc_t x, mpfr_prec_t prec_re, mpfr_prec_t prec_im) +{ + mpfr_init2 (mpc_realref(x), prec_re); + mpfr_init2 (mpc_imagref(x), prec_im); +} diff --git a/mpc/src/inp_str.c b/mpc/src/inp_str.c new file mode 100644 index 0000000000..695a3adff6 --- /dev/null +++ b/mpc/src/inp_str.c @@ -0,0 +1,239 @@ +/* mpc_inp_str -- Input a complex number from a given stream. + +Copyright (C) 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for FILE */ +#include <ctype.h> +#include <string.h> +#include "mpc-impl.h" + +static size_t +skip_whitespace (FILE *stream) +{ + int c = getc (stream); + size_t size = 0; + while (c != EOF && isspace ((unsigned char) c)) { + c = getc (stream); + size++; + } + if (c != EOF) + ungetc (c, stream); + return size; +} + +/* Extract from stream the longest string made up of alphanumeric char and + '_' (i.e. n-char-sequence). + The user must free the returned string. */ +static char * +extract_suffix (FILE *stream) +{ + int c; + size_t nread = 0; + size_t strsize = 100; + char *str = mpc_alloc_str (strsize); + + c = getc (stream); + while (isalnum ((unsigned char) c) || c == '_') { + str [nread] = (char) c; + nread++; + if (nread == strsize) { + str = mpc_realloc_str (str, strsize, 2 * strsize); + strsize *= 2; + } + c = getc (stream); + } + + str = mpc_realloc_str (str, strsize, nread + 1); + strsize = nread + 1; + str [nread] = '\0'; + + if (c != EOF) + ungetc (c, stream); + return str; +} + + +/* Extract from the stream the longest string of characters which are neither + whitespace nor brackets (except for an optional bracketed n-char_sequence + directly following nan or @nan@ independently of case). + The user must free the returned string. */ +static char * +extract_string (FILE *stream) +{ + int c; + size_t nread = 0; + size_t strsize = 100; + char *str = mpc_alloc_str (strsize); + size_t lenstr; + + c = getc (stream); + while (c != EOF && c != '\n' + && !isspace ((unsigned char) c) + && c != '(' && c != ')') { + str [nread] = (char) c; + nread++; + if (nread == strsize) { + str = mpc_realloc_str (str, strsize, 2 * strsize); + strsize *= 2; + } + c = getc (stream); + } + + str = mpc_realloc_str (str, strsize, nread + 1); + strsize = nread + 1; + str [nread] = '\0'; + + if (nread == 0) + return str; + + lenstr = nread; + + if (c == '(') { + size_t n; + char *suffix; + int ret; + + /* (n-char-sequence) only after a NaN */ + if ((nread != 3 + || tolower ((unsigned char) (str[0])) != 'n' + || tolower ((unsigned char) (str[1])) != 'a' + || tolower ((unsigned char) (str[2])) != 'n') + && (nread != 5 + || str[0] != '@' + || tolower ((unsigned char) (str[1])) != 'n' + || tolower ((unsigned char) (str[2])) != 'a' + || tolower ((unsigned char) (str[3])) != 'n' + || str[4] != '@')) { + ungetc (c, stream); + return str; + } + + suffix = extract_suffix (stream); + nread += strlen (suffix) + 1; + if (nread >= strsize) { + str = mpc_realloc_str (str, strsize, nread + 1); + strsize = nread + 1; + } + + /* Warning: the sprintf does not allow overlap between arguments. */ + ret = sprintf (str + lenstr, "(%s", suffix); + MPC_ASSERT (ret >= 0); + n = lenstr + (size_t) ret; + MPC_ASSERT (n == nread); + + c = getc (stream); + if (c == ')') { + str = mpc_realloc_str (str, strsize, nread + 2); + strsize = nread + 2; + str [nread] = (char) c; + str [nread+1] = '\0'; + nread++; + } + else if (c != EOF) + ungetc (c, stream); + + mpc_free_str (suffix); + } + else if (c != EOF) + ungetc (c, stream); + + return str; +} + + +int +mpc_inp_str (mpc_ptr rop, FILE *stream, size_t *read, int base, +mpc_rnd_t rnd_mode) +{ + size_t white, nread = 0; + int inex = -1; + int c; + char *str; + + if (stream == NULL) + stream = stdin; + + white = skip_whitespace (stream); + c = getc (stream); + if (c != EOF) { + if (c == '(') { + char *real_str; + char *imag_str; + size_t n; + int ret; + + nread++; /* the opening parenthesis */ + white = skip_whitespace (stream); + real_str = extract_string (stream); + nread += strlen(real_str); + + c = getc (stream); + if (!isspace ((unsigned int) c)) { + if (c != EOF) + ungetc (c, stream); + mpc_free_str (real_str); + goto error; + } + else + ungetc (c, stream); + + white += skip_whitespace (stream); + imag_str = extract_string (stream); + nread += strlen (imag_str); + + str = mpc_alloc_str (nread + 2); + ret = sprintf (str, "(%s %s", real_str, imag_str); + MPC_ASSERT (ret >= 0); + n = (size_t) ret; + MPC_ASSERT (n == nread + 1); + mpc_free_str (real_str); + mpc_free_str (imag_str); + + white += skip_whitespace (stream); + c = getc (stream); + if (c == ')') { + str = mpc_realloc_str (str, nread +2, nread + 3); + str [nread+1] = (char) c; + str [nread+2] = '\0'; + nread++; + } + else if (c != EOF) + ungetc (c, stream); + } + else { + if (c != EOF) + ungetc (c, stream); + str = extract_string (stream); + nread += strlen (str); + } + + inex = mpc_set_str (rop, str, base, rnd_mode); + + mpc_free_str (str); + } + +error: + if (inex == -1) { + mpfr_set_nan (mpc_realref(rop)); + mpfr_set_nan (mpc_imagref(rop)); + } + if (read != NULL) + *read = white + nread; + return inex; +} diff --git a/mpc/src/log.c b/mpc/src/log.c new file mode 100644 index 0000000000..ad1d448053 --- /dev/null +++ b/mpc/src/log.c @@ -0,0 +1,217 @@ +/* mpc_log -- Take the logarithm of a complex number. + +Copyright (C) 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + +int +mpc_log (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd){ + int ok, underflow = 0; + mpfr_srcptr x, y; + mpfr_t v, w; + mpfr_prec_t prec; + int loops; + int re_cmp, im_cmp; + int inex_re, inex_im; + int err; + mpfr_exp_t expw; + int sgnw; + + /* special values: NaN and infinities */ + if (!mpc_fin_p (op)) { + if (mpfr_nan_p (mpc_realref (op))) { + if (mpfr_inf_p (mpc_imagref (op))) + mpfr_set_inf (mpc_realref (rop), +1); + else + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex_im = 0; /* Inf/NaN is exact */ + } + else if (mpfr_nan_p (mpc_imagref (op))) { + if (mpfr_inf_p (mpc_realref (op))) + mpfr_set_inf (mpc_realref (rop), +1); + else + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex_im = 0; /* Inf/NaN is exact */ + } + else /* We have an infinity in at least one part. */ { + inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), + MPC_RND_IM (rnd)); + mpfr_set_inf (mpc_realref (rop), +1); + } + return MPC_INEX(0, inex_im); + } + + /* special cases: real and purely imaginary numbers */ + re_cmp = mpfr_cmp_ui (mpc_realref (op), 0); + im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0); + if (im_cmp == 0) { + if (re_cmp == 0) { + inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), + MPC_RND_IM (rnd)); + mpfr_set_inf (mpc_realref (rop), -1); + inex_re = 0; /* -Inf is exact */ + } + else if (re_cmp > 0) { + inex_re = mpfr_log (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); + } + else { + /* op = x + 0*y; let w = -x = |x| */ + int negative_zero; + mpfr_rnd_t rnd_im; + + negative_zero = mpfr_signbit (mpc_imagref (op)); + if (negative_zero) + rnd_im = INV_RND (MPC_RND_IM (rnd)); + else + rnd_im = MPC_RND_IM (rnd); + w [0] = *mpc_realref (op); + MPFR_CHANGE_SIGN (w); + inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd)); + inex_im = mpfr_const_pi (mpc_imagref (rop), rnd_im); + if (negative_zero) { + mpc_conj (rop, rop, MPC_RNDNN); + inex_im = -inex_im; + } + } + return MPC_INEX(inex_re, inex_im); + } + else if (re_cmp == 0) { + if (im_cmp > 0) { + inex_re = mpfr_log (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd)); + inex_im = mpfr_const_pi (mpc_imagref (rop), MPC_RND_IM (rnd)); + /* division by 2 does not change the ternary flag */ + mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); + } + else { + w [0] = *mpc_imagref (op); + MPFR_CHANGE_SIGN (w); + inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd)); + inex_im = mpfr_const_pi (mpc_imagref (rop), INV_RND (MPC_RND_IM (rnd))); + /* division by 2 does not change the ternary flag */ + mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); + mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN); + inex_im = -inex_im; /* negate the ternary flag */ + } + return MPC_INEX(inex_re, inex_im); + } + + prec = MPC_PREC_RE(rop); + mpfr_init2 (w, 2); + /* let op = x + iy; log = 1/2 log (x^2 + y^2) + i atan2 (y, x) */ + /* loop for the real part: 1/2 log (x^2 + y^2), fast, but unsafe */ + /* implementation */ + ok = 0; + for (loops = 1; !ok && loops <= 2; loops++) { + prec += mpc_ceil_log2 (prec) + 4; + mpfr_set_prec (w, prec); + + mpc_abs (w, op, GMP_RNDN); + /* error 0.5 ulp */ + if (mpfr_inf_p (w)) + /* intermediate overflow; the logarithm may be representable. + Intermediate underflow is impossible. */ + break; + + mpfr_log (w, w, GMP_RNDN); + /* generic error of log: (2^(- exp(w)) + 0.5) ulp */ + + if (mpfr_zero_p (w)) + /* impossible to round, switch to second algorithm */ + break; + + err = MPC_MAX (-mpfr_get_exp (w), 0) + 1; + /* number of lost digits */ + ok = mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ, + mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN)); + } + + if (!ok) { + prec = MPC_PREC_RE(rop); + mpfr_init2 (v, 2); + /* compute 1/2 log (x^2 + y^2) = log |x| + 1/2 * log (1 + (y/x)^2) + if |x| >= |y|; otherwise, exchange x and y */ + if (mpfr_cmpabs (mpc_realref (op), mpc_imagref (op)) >= 0) { + x = mpc_realref (op); + y = mpc_imagref (op); + } + else { + x = mpc_imagref (op); + y = mpc_realref (op); + } + + do { + prec += mpc_ceil_log2 (prec) + 4; + mpfr_set_prec (v, prec); + mpfr_set_prec (w, prec); + + mpfr_div (v, y, x, GMP_RNDD); /* error 1 ulp */ + mpfr_sqr (v, v, GMP_RNDD); + /* generic error of multiplication: + 1 + 2*1*(2+1*2^(1-prec)) <= 5.0625 since prec >= 6 */ + mpfr_log1p (v, v, GMP_RNDD); + /* error 1 + 4*5.0625 = 21.25 , see algorithms.tex */ + mpfr_div_2ui (v, v, 1, GMP_RNDD); + /* If the result is 0, then there has been an underflow somewhere. */ + + mpfr_abs (w, x, GMP_RNDN); /* exact */ + mpfr_log (w, w, GMP_RNDN); /* error 0.5 ulp */ + expw = mpfr_get_exp (w); + sgnw = mpfr_signbit (w); + + mpfr_add (w, w, v, GMP_RNDN); + if (!sgnw) /* v is positive, so no cancellation; + error 22.25 ulp; error counts lost bits */ + err = 5; + else + err = MPC_MAX (5 + mpfr_get_exp (v), + /* 21.25 ulp (v) rewritten in ulp (result, now in w) */ + -1 + expw - mpfr_get_exp (w) + /* 0.5 ulp (previous w), rewritten in ulp (result) */ + ) + 2; + + /* handle one special case: |x|=1, and (y/x)^2 underflows; + then 1/2*log(x^2+y^2) \approx 1/2*y^2 also underflows. */ + if ( (mpfr_cmp_si (x, -1) == 0 || mpfr_cmp_ui (x, 1) == 0) + && mpfr_zero_p (w)) + underflow = 1; + + } while (!underflow && + !mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ, + mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN))); + mpfr_clear (v); + } + + /* imaginary part */ + inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op), + MPC_RND_IM (rnd)); + + /* set the real part; cannot be done before if rop==op */ + if (underflow) + /* create underflow in result */ + inex_re = mpfr_set_ui_2exp (mpc_realref (rop), 1, + mpfr_get_emin_min () - 2, MPC_RND_RE (rnd)); + else + inex_re = mpfr_set (mpc_realref (rop), w, MPC_RND_RE (rnd)); + mpfr_clear (w); + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/log10.c b/mpc/src/log10.c new file mode 100644 index 0000000000..4e77aafe15 --- /dev/null +++ b/mpc/src/log10.c @@ -0,0 +1,286 @@ +/* mpc_log10 -- Take the base-10 logarithm of a complex number. + +Copyright (C) 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <limits.h> /* for CHAR_BIT */ +#include "mpc-impl.h" + +/* Auxiliary functions which implement Ziv's strategy for special cases. + if flag = 0: compute only real part + if flag = 1: compute only imaginary + Exact cases should be dealt with separately. */ +static int +mpc_log10_aux (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd, int flag, int nb) +{ + mp_prec_t prec = (MPFR_PREC_MIN > 4) ? MPFR_PREC_MIN : 4; + mpc_t tmp; + mpfr_t log10; + int ok = 0, ret; + + prec = mpfr_get_prec ((flag == 0) ? mpc_realref (rop) : mpc_imagref (rop)); + prec += 10; + mpc_init2 (tmp, prec); + mpfr_init2 (log10, prec); + while (ok == 0) + { + mpfr_set_ui (log10, 10, GMP_RNDN); /* exact since prec >= 4 */ + mpfr_log (log10, log10, GMP_RNDN); + /* In each case we have two roundings, thus the final value is + x * (1+u)^2 where x is the exact value, and |u| <= 2^(-prec-1). + Thus the error is always less than 3 ulps. */ + switch (nb) + { + case 0: /* imag <- atan2(y/x) */ + mpfr_atan2 (mpc_imagref (tmp), mpc_imagref (op), mpc_realref (op), + MPC_RND_IM (rnd)); + mpfr_div (mpc_imagref (tmp), mpc_imagref (tmp), log10, GMP_RNDN); + ok = mpfr_can_round (mpc_imagref (tmp), prec - 2, GMP_RNDN, + GMP_RNDZ, MPC_PREC_IM(rop) + + (MPC_RND_IM (rnd) == GMP_RNDN)); + if (ok) + ret = mpfr_set (mpc_imagref (rop), mpc_imagref (tmp), + MPC_RND_IM (rnd)); + break; + case 1: /* real <- log(x) */ + mpfr_log (mpc_realref (tmp), mpc_realref (op), MPC_RND_RE (rnd)); + mpfr_div (mpc_realref (tmp), mpc_realref (tmp), log10, GMP_RNDN); + ok = mpfr_can_round (mpc_realref (tmp), prec - 2, GMP_RNDN, + GMP_RNDZ, MPC_PREC_RE(rop) + + (MPC_RND_RE (rnd) == GMP_RNDN)); + if (ok) + ret = mpfr_set (mpc_realref (rop), mpc_realref (tmp), + MPC_RND_RE (rnd)); + break; + case 2: /* imag <- pi */ + mpfr_const_pi (mpc_imagref (tmp), MPC_RND_IM (rnd)); + mpfr_div (mpc_imagref (tmp), mpc_imagref (tmp), log10, GMP_RNDN); + ok = mpfr_can_round (mpc_imagref (tmp), prec - 2, GMP_RNDN, + GMP_RNDZ, MPC_PREC_IM(rop) + + (MPC_RND_IM (rnd) == GMP_RNDN)); + if (ok) + ret = mpfr_set (mpc_imagref (rop), mpc_imagref (tmp), + MPC_RND_IM (rnd)); + break; + } + prec += prec / 2; + mpc_set_prec (tmp, prec); + mpfr_set_prec (log10, prec); + } + mpc_clear (tmp); + mpfr_clear (log10); + return ret; +} + +int +mpc_log10 (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + int ok = 0, loops = 0, re_cmp, im_cmp, inex_re, inex_im, negative_zero; + mpfr_t w; + mpfr_prec_t prec; + mpfr_rnd_t rnd_im; + mpc_t ww; + mpc_rnd_t invrnd; + + /* special values: NaN and infinities: same as mpc_log */ + if (!mpc_fin_p (op)) /* real or imaginary parts are NaN or Inf */ + { + if (mpfr_nan_p (mpc_realref (op))) + { + if (mpfr_inf_p (mpc_imagref (op))) + /* (NaN, Inf) -> (+Inf, NaN) */ + mpfr_set_inf (mpc_realref (rop), +1); + else + /* (NaN, xxx) -> (NaN, NaN) */ + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex_im = 0; /* Inf/NaN is exact */ + } + else if (mpfr_nan_p (mpc_imagref (op))) + { + if (mpfr_inf_p (mpc_realref (op))) + /* (Inf, NaN) -> (+Inf, NaN) */ + mpfr_set_inf (mpc_realref (rop), +1); + else + /* (xxx, NaN) -> (NaN, NaN) */ + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex_im = 0; /* Inf/NaN is exact */ + } + else /* We have an infinity in at least one part. */ + { + /* (+Inf, y) -> (+Inf, 0) for finite positive-signed y */ + if (mpfr_inf_p (mpc_realref (op)) && mpfr_signbit (mpc_realref (op)) + == 0 && mpfr_number_p (mpc_imagref (op))) + inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), + mpc_realref (op), MPC_RND_IM (rnd)); + else + /* (xxx, Inf) -> (+Inf, atan2(Inf/xxx)) + (Inf, yyy) -> (+Inf, atan2(yyy/Inf)) */ + inex_im = mpc_log10_aux (rop, op, rnd, 1, 0); + mpfr_set_inf (mpc_realref (rop), +1); + } + return MPC_INEX(0, inex_im); + } + + /* special cases: real and purely imaginary numbers */ + re_cmp = mpfr_cmp_ui (mpc_realref (op), 0); + im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0); + if (im_cmp == 0) /* Im(op) = 0 */ + { + if (re_cmp == 0) /* Re(op) = 0 */ + { + if (mpfr_signbit (mpc_realref (op)) == 0) + inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), + mpc_realref (op), MPC_RND_IM (rnd)); + else + inex_im = mpc_log10_aux (rop, op, rnd, 1, 0); + mpfr_set_inf (mpc_realref (rop), -1); + inex_re = 0; /* -Inf is exact */ + } + else if (re_cmp > 0) + { + inex_re = mpfr_log10 (mpc_realref (rop), mpc_realref (op), + MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), + MPC_RND_IM (rnd)); + } + else /* log10(x + 0*i) for negative x */ + { /* op = x + 0*i; let w = -x = |x| */ + negative_zero = mpfr_signbit (mpc_imagref (op)); + if (negative_zero) + rnd_im = INV_RND (MPC_RND_IM (rnd)); + else + rnd_im = MPC_RND_IM (rnd); + ww->re[0] = *mpc_realref (op); + MPFR_CHANGE_SIGN (ww->re); + ww->im[0] = *mpc_imagref (op); + if (mpfr_cmp_ui (ww->re, 1) == 0) + inex_re = mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd)); + else + inex_re = mpc_log10_aux (rop, ww, rnd, 0, 1); + inex_im = mpc_log10_aux (rop, op, MPC_RND (0,rnd_im), 1, 2); + if (negative_zero) + { + mpc_conj (rop, rop, MPC_RNDNN); + inex_im = -inex_im; + } + } + return MPC_INEX(inex_re, inex_im); + } + else if (re_cmp == 0) + { + if (im_cmp > 0) + { + inex_re = mpfr_log10 (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd)); + inex_im = mpc_log10_aux (rop, op, rnd, 1, 2); + /* division by 2 does not change the ternary flag */ + mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); + } + else + { + w [0] = *mpc_imagref (op); + MPFR_CHANGE_SIGN (w); + inex_re = mpfr_log10 (mpc_realref (rop), w, MPC_RND_RE (rnd)); + invrnd = MPC_RND (0, INV_RND (MPC_RND_IM (rnd))); + inex_im = mpc_log10_aux (rop, op, invrnd, 1, 2); + /* division by 2 does not change the ternary flag */ + mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN); + mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN); + inex_im = -inex_im; /* negate the ternary flag */ + } + return MPC_INEX(inex_re, inex_im); + } + + /* generic case: neither Re(op) nor Im(op) is NaN, Inf or zero */ + prec = MPC_PREC_RE(rop); + mpfr_init2 (w, prec); + mpc_init2 (ww, prec); + /* let op = x + iy; compute log(op)/log(10) */ + while (ok == 0) + { + loops ++; + prec += (loops <= 2) ? mpc_ceil_log2 (prec) + 4 : prec / 2; + mpfr_set_prec (w, prec); + mpc_set_prec (ww, prec); + + mpc_log (ww, op, MPC_RNDNN); + mpfr_set_ui (w, 10, GMP_RNDN); /* exact since prec >= 4 */ + mpfr_log (w, w, GMP_RNDN); + mpc_div_fr (ww, ww, w, MPC_RNDNN); + + ok = mpfr_can_round (mpc_realref (ww), prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE(rop) + (MPC_RND_RE (rnd) == GMP_RNDN)); + + /* Special code to deal with cases where the real part of log10(x+i*y) + is exact, like x=3 and y=1. Since Re(log10(x+i*y)) = log10(x^2+y^2)/2 + this happens whenever x^2+y^2 is a nonnegative power of 10. + Indeed x^2+y^2 cannot equal 10^(a/2^b) for a, b integers, a odd, b>0, + since x^2+y^2 is rational, and 10^(a/2^b) is irrational. + Similarly, for b=0, x^2+y^2 cannot equal 10^a for a < 0 since x^2+y^2 + is a rational with denominator a power of 2. + Now let x^2+y^2 = 10^s. Without loss of generality we can assume + x = u/2^e and y = v/2^e with u, v, e integers: u^2+v^2 = 10^s*2^(2e) + thus u^2+v^2 = 0 mod 2^(2e). By recurrence on e, necessarily + u = v = 0 mod 2^e, thus x and y are necessarily integers. + */ + if ((ok == 0) && (loops == 1) && mpfr_integer_p (mpc_realref (op)) && + mpfr_integer_p (mpc_imagref (op))) + { + mpz_t x, y; + unsigned long s, v; + + mpz_init (x); + mpz_init (y); + mpfr_get_z (x, mpc_realref (op), GMP_RNDN); /* exact */ + mpfr_get_z (y, mpc_imagref (op), GMP_RNDN); /* exact */ + mpz_mul (x, x, x); + mpz_mul (y, y, y); + mpz_add (x, x, y); /* x^2+y^2 */ + v = mpz_scan1 (x, 0); + /* if x = 10^s then necessarily s = v */ + s = mpz_sizeinbase (x, 10); + /* since s is either the number of digits of x or one more, + then x = 10^(s-1) or 10^(s-2) */ + if (s == v + 1 || s == v + 2) + { + mpz_div_2exp (x, x, v); + mpz_ui_pow_ui (y, 5, v); + if (mpz_cmp (y, x) == 0) /* Re(log10(x+i*y)) is exactly v/2 */ + { + /* we reset the precision of Re(ww) so that v can be + represented exactly */ + mpfr_set_prec (mpc_realref (ww), sizeof(unsigned long)*CHAR_BIT); + mpfr_set_ui_2exp (mpc_realref (ww), v, -1, GMP_RNDN); /* exact */ + ok = 1; + } + } + mpz_clear (x); + mpz_clear (y); + } + + ok = ok && mpfr_can_round (mpc_imagref (ww), prec-2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM(rop) + (MPC_RND_IM (rnd) == GMP_RNDN)); + } + + inex_re = mpfr_set (mpc_realref(rop), mpc_realref (ww), MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref(rop), mpc_imagref (ww), MPC_RND_IM (rnd)); + mpfr_clear (w); + mpc_clear (ww); + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/logging.c b/mpc/src/logging.c new file mode 100644 index 0000000000..79ed0338e7 --- /dev/null +++ b/mpc/src/logging.c @@ -0,0 +1,147 @@ +/* logging.c -- "Dummy" functions logging calls to real mpc functions. + +Copyright (C) 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "config.h" +#include <stdio.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#define __MPC_LIBRARY_BUILD + /* to indicate we are inside the library build; needed here since mpc-log.h + includes mpc.h and not mpc-impl.h */ +#include "mpc-log.h" + +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +typedef int (*c_c_func_ptr) (mpc_ptr, mpc_srcptr, mpc_rnd_t); +typedef int (*c_cc_func_ptr) (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +typedef int (*c_ccc_func_ptr) (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +typedef int (*cc_c_func_ptr) (mpc_ptr, mpc_ptr, mpc_srcptr, mpc_rnd_t, mpc_rnd_t); + +#define MPC_LOGGING_OUT_PREC(z) \ + do { \ + fprintf (stderr, " %li %li", (long) mpfr_get_prec (mpc_realref (z)), \ + (long) mpfr_get_prec (mpc_imagref (z))); \ + } while (0); + +#define MPC_LOGGING_OUT_C(z) \ + do { \ + MPC_LOGGING_OUT_PREC (z); \ + fprintf (stderr, " "); \ + mpc_out_str (stderr, 16, 0, z, MPC_RNDNN); \ + } while (0); + +#define MPC_LOGGING_FUNC_TYPE(funcname, type) \ + do { \ + fprintf (stderr, "mpc_"#funcname" "#type); \ + } while (0); + +#define MPC_LOGGING_C_C(funcname) \ +__MPC_DECLSPEC int mpc_log_##funcname (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) \ +{ \ + static c_c_func_ptr func = NULL; \ + if (func == NULL) \ + func = (c_c_func_ptr) (intptr_t) dlsym (NULL, "mpc_"#funcname); \ + MPC_LOGGING_FUNC_TYPE (funcname, c_c); \ + MPC_LOGGING_OUT_PREC (rop); \ + MPC_LOGGING_OUT_C (op); \ + fprintf (stderr, "\n"); \ + return func (rop, op, rnd); \ +} + +#define MPC_LOGGING_C_CC(funcname) \ +__MPC_DECLSPEC int mpc_log_##funcname (mpc_ptr rop, mpc_srcptr op1, mpc_srcptr op2, mpc_rnd_t rnd) \ +{ \ + static c_cc_func_ptr func = NULL; \ + if (func == NULL) \ + func = (c_cc_func_ptr) (intptr_t) dlsym (NULL, "mpc_"#funcname); \ + MPC_LOGGING_FUNC_TYPE (funcname, c_cc); \ + MPC_LOGGING_OUT_PREC (rop); \ + MPC_LOGGING_OUT_C (op1); \ + MPC_LOGGING_OUT_C (op2); \ + fprintf (stderr, "\n"); \ + return func (rop, op1, op2, rnd); \ +} + +#define MPC_LOGGING_C_CCC(funcname) \ +__MPC_DECLSPEC int mpc_log_##funcname (mpc_ptr rop, mpc_srcptr op1, mpc_srcptr op2, mpc_srcptr op3, mpc_rnd_t rnd) \ +{ \ + static c_ccc_func_ptr func = NULL; \ + if (func == NULL) \ + func = (c_ccc_func_ptr) (intptr_t) dlsym (NULL, "mpc_"#funcname); \ + MPC_LOGGING_FUNC_TYPE (funcname, c_ccc); \ + MPC_LOGGING_OUT_PREC (rop); \ + MPC_LOGGING_OUT_C (op1); \ + MPC_LOGGING_OUT_C (op2); \ + MPC_LOGGING_OUT_C (op3); \ + fprintf (stderr, "\n"); \ + return func (rop, op1, op2, op3, rnd); \ +} + +#define MPC_LOGGING_CC_C(funcname) \ +__MPC_DECLSPEC int mpc_log_##funcname (mpc_ptr rop1, mpc_ptr rop2, mpc_srcptr op, mpc_rnd_t rnd1, mpc_rnd_t rnd2) \ +{ \ + static cc_c_func_ptr func = NULL; \ + if (func == NULL) \ + func = (cc_c_func_ptr) (intptr_t) dlsym (NULL, "mpc_"#funcname); \ + MPC_LOGGING_FUNC_TYPE (funcname, cc_c); \ + MPC_LOGGING_OUT_PREC (rop1); \ + MPC_LOGGING_OUT_PREC (rop2); \ + MPC_LOGGING_OUT_C (op); \ + fprintf (stderr, "\n"); \ + return func (rop1, rop2, op, rnd1, rnd2); \ +} + +MPC_LOGGING_C_C (sqr) +MPC_LOGGING_C_C (conj) +MPC_LOGGING_C_C (neg) +MPC_LOGGING_C_C (sqrt) +MPC_LOGGING_C_C (proj) +MPC_LOGGING_C_C (exp) +MPC_LOGGING_C_C (log) +MPC_LOGGING_C_C (sin) +MPC_LOGGING_C_C (cos) +MPC_LOGGING_C_C (tan) +MPC_LOGGING_C_C (sinh) +MPC_LOGGING_C_C (cosh) +MPC_LOGGING_C_C (tanh) +MPC_LOGGING_C_C (asin) +MPC_LOGGING_C_C (acos) +MPC_LOGGING_C_C (atan) +MPC_LOGGING_C_C (asinh) +MPC_LOGGING_C_C (acosh) +MPC_LOGGING_C_C (atanh) + +MPC_LOGGING_C_CC (add) +MPC_LOGGING_C_CC (sub) +MPC_LOGGING_C_CC (mul) +MPC_LOGGING_C_CC (div) +MPC_LOGGING_C_CC (pow) + +MPC_LOGGING_C_CCC (fma) + +MPC_LOGGING_CC_C (sin_cos) diff --git a/mpc/src/mem.c b/mpc/src/mem.c new file mode 100644 index 0000000000..b4c327c079 --- /dev/null +++ b/mpc/src/mem.c @@ -0,0 +1,46 @@ +/* wrapper functions to allocate, reallocate and free memory + +Copyright (C) 2009 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <string.h> /* for strlen */ +#include "mpc-impl.h" + +char * +mpc_alloc_str (size_t len) +{ + void * (*allocfunc) (size_t); + mp_get_memory_functions (&allocfunc, NULL, NULL); + return (char *) ((*allocfunc) (len)); +} + +char * +mpc_realloc_str (char * str, size_t oldlen, size_t newlen) +{ + void * (*reallocfunc) (void *, size_t, size_t); + mp_get_memory_functions (NULL, &reallocfunc, NULL); + return (char *) ((*reallocfunc) (str, oldlen, newlen)); +} + +void +mpc_free_str (char *str) +{ + void (*freefunc) (void *, size_t); + mp_get_memory_functions (NULL, NULL, &freefunc); + (*freefunc) (str, strlen (str) + 1); +} diff --git a/mpc/src/mpc-impl.h b/mpc/src/mpc-impl.h new file mode 100644 index 0000000000..b2aaa90eb3 --- /dev/null +++ b/mpc/src/mpc-impl.h @@ -0,0 +1,194 @@ +/* mpc-impl.h -- Internal include file for mpc. + +Copyright (C) 2002, 2004, 2005, 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#ifndef __MPC_IMPL_H +#define __MPC_IMPL_H +#define __MPC_LIBRARY_BUILD + /* to indicate we are inside the library build */ + +#include "config.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include "mpc.h" + +/* + * Miscellaneous useful macros + */ + +#define MPC_MIN(h,i) ((h) < (i) ? (h) : (i)) +#define MPC_MAX(h,i) ((h) > (i) ? (h) : (i)) + +/* Safe absolute value (to avoid possible integer overflow) */ +/* type is the target (unsigned) type (copied from mpfr-impl.h) */ +#ifdef SAFE_ABS +#undef SAFE_ABS +#endif +#define SAFE_ABS(type,x) ((x) >= 0 ? (type)(x) : -(type)(x)) + + +/* + * MPFR constants and macros + */ + +#ifndef BITS_PER_MP_LIMB +#define BITS_PER_MP_LIMB mp_bits_per_limb +#endif + +#define MPFR_SIGNBIT(x) (mpfr_signbit (x) ? -1 : 1) +#define MPC_MPFR_SIGN(x) (mpfr_zero_p (x) ? 0 : MPFR_SIGNBIT (x)) + /* should be called MPFR_SIGN, but this is taken in mpfr.h */ +#define MPFR_CHANGE_SIGN(x) mpfr_neg(x,x,GMP_RNDN) +#define MPFR_COPYSIGN(x,y,z,rnd) (mpfr_nan_p (z) ? \ + mpfr_setsign (x, y, 0, rnd) : \ + mpfr_copysign (x, y, z, rnd)) + /* work around spurious signs in nan */ +#define MPFR_ADD_ONE_ULP(x) mpfr_add_one_ulp (x, GMP_RNDN) +#define MPFR_SUB_ONE_ULP(x) mpfr_sub_one_ulp (x, GMP_RNDN) + /* drop unused rounding mode from macroes */ +#define MPFR_SWAP(a,b) do { mpfr_srcptr tmp; tmp = a; a = b; b = tmp; } while (0) + + +/* + * Macro implementing rounding away from zero, to ease compatibility with + * mpfr < 3. f is the complete function call with a rounding mode of + * MPFR_RNDA, rop the name of the variable containing the result; it is + * already contained in f, but needs to be repeated so that the macro can + * modify the variable. + * Usage: replace each call to a function such as + * mpfr_add (rop, a, b, MPFR_RNDA) + * by + * ROUND_AWAY (mpfr_add (rop, a, b, MPFR_RNDA), rop) +*/ +#if MPFR_VERSION_MAJOR < 3 + /* round towards zero, add 1 ulp if not exact */ +#define MPFR_RNDA GMP_RNDZ +#define ROUND_AWAY(f,rop) \ + ((f) ? MPFR_ADD_ONE_ULP (rop), MPFR_SIGNBIT (rop) : 0) +#else +#define ROUND_AWAY(f,rop) \ + (f) +#endif /* mpfr < 3 */ + +#if MPFR_VERSION_MAJOR < 3 +/* declare missing functions, defined in get_version.c */ +__MPC_DECLSPEC void mpfr_set_zero (mpfr_ptr, int); +__MPC_DECLSPEC int mpfr_regular_p (mpfr_srcptr); +#endif /* mpfr < 3 */ + + +/* + * MPC macros + */ + +#define MPC_PREC_RE(x) (mpfr_get_prec(mpc_realref(x))) +#define MPC_PREC_IM(x) (mpfr_get_prec(mpc_imagref(x))) +#define MPC_MAX_PREC(x) MPC_MAX(MPC_PREC_RE(x), MPC_PREC_IM(x)) + +#define INV_RND(r) \ + (((r) == GMP_RNDU) ? GMP_RNDD : (((r) == GMP_RNDD) ? GMP_RNDU : (r))) + +#define mpc_inf_p(z) (mpfr_inf_p(mpc_realref(z))||mpfr_inf_p(mpc_imagref(z))) + /* Convention in C99 (G.3): z is regarded as an infinity if at least one of + its parts is infinite */ +#define mpc_zero_p(z) (mpfr_zero_p(mpc_realref(z))&&mpfr_zero_p(mpc_imagref(z))) + /* Convention in C99 (G.3): z is regarded as a zero if each of its parts is + a zero */ +#define mpc_fin_p(z) (mpfr_number_p(mpc_realref(z))&&mpfr_number_p(mpc_imagref(z))) + /* Convention in C99 (G.3): z is regarded as finite if both its parts are */ +#define mpc_nan_p(z) ((mpfr_nan_p(mpc_realref(z)) && !mpfr_inf_p(mpc_imagref(z))) || (mpfr_nan_p(mpc_imagref(z)) && !mpfr_inf_p(mpc_realref(z)))) + /* Consider as NaN all other numbers containing at least one NaN */ + + +/* + * ASSERT macros + */ + +#ifdef NDEBUG +#define MPC_ASSERT(expr) \ + do { \ + } while (0) +#else +#define MPC_ASSERT(expr) \ + do { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: MPC assertion failed: %s\n", \ + __FILE__, __LINE__, #expr); \ + abort(); \ + } \ + } while (0) +#endif + + +/* + * Debug macros + */ + +#define MPC_OUT(x) \ +do { \ + printf (#x "[%lu,%lu]=", (unsigned long int) MPC_PREC_RE (x), \ + (unsigned long int) MPC_PREC_IM (x)); \ + mpc_out_str (stdout, 2, 0, x, MPC_RNDNN); \ + printf ("\n"); \ +} while (0) + +#define MPFR_OUT(x) \ +do { \ + printf (#x "[%lu]=", (unsigned long int) mpfr_get_prec (x)); \ + mpfr_out_str (stdout, 2, 0, x, GMP_RNDN); \ + printf ("\n"); \ +} while (0) + + +/* + * Constants + */ + +#ifndef MUL_KARATSUBA_THRESHOLD +#define MUL_KARATSUBA_THRESHOLD 23 +#endif + + +/* + * Define internal functions + */ + +#if defined (__cplusplus) +extern "C" { +#endif + + +__MPC_DECLSPEC int mpc_mul_naive (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_karatsuba (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_fma_naive (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_usi (mpc_ptr, mpc_srcptr, unsigned long, int, mpc_rnd_t); +__MPC_DECLSPEC char* mpc_alloc_str (size_t); +__MPC_DECLSPEC char* mpc_realloc_str (char*, size_t, size_t); +__MPC_DECLSPEC void mpc_free_str (char*); +__MPC_DECLSPEC mpfr_prec_t mpc_ceil_log2 (mpfr_prec_t); +__MPC_DECLSPEC int set_pi_over_2 (mpfr_ptr, int, mpfr_rnd_t); + +#if defined (__cplusplus) +} +#endif + + +#endif diff --git a/mpc/src/mpc-log.h b/mpc/src/mpc-log.h new file mode 100644 index 0000000000..78c98d2da4 --- /dev/null +++ b/mpc/src/mpc-log.h @@ -0,0 +1,51 @@ +/* mpc-log.h -- Include file to enable function call logging; replaces mpc.h. + +Copyright (C) 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#define mpc_sqr mpc_log_sqr +#define mpc_conj mpc_log_conj +#define mpc_neg mpc_log_neg +#define mpc_sqrt mpc_log_sqrt +#define mpc_proj mpc_log_proj +#define mpc_exp mpc_log_exp +#define mpc_log mpc_log_log +#define mpc_sin mpc_log_sin +#define mpc_cos mpc_log_cos +#define mpc_tan mpc_log_tan +#define mpc_sinh mpc_log_sinh +#define mpc_cosh mpc_log_cosh +#define mpc_tanh mpc_log_tanh +#define mpc_asin mpc_log_asin +#define mpc_acos mpc_log_acos +#define mpc_atan mpc_log_atan +#define mpc_asinh mpc_log_asinh +#define mpc_acosh mpc_log_acosh +#define mpc_atanh mpc_log_atanh + +#define mpc_add mpc_log_add +#define mpc_sub mpc_log_sub +#define mpc_mul mpc_log_mul +#define mpc_div mpc_log_div +#define mpc_pow mpc_log_pow + +#define mpc_fma mpc_log_fma + +#define mpc_sin_cos mpc_log_sin_cos + +#include "mpc.h" diff --git a/mpc/src/mpc.h b/mpc/src/mpc.h new file mode 100644 index 0000000000..f6e1beefe2 --- /dev/null +++ b/mpc/src/mpc.h @@ -0,0 +1,269 @@ +/* mpc.h -- Include file for mpc. + +Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#ifndef __MPC_H +#define __MPC_H + +#include "gmp.h" +#include "mpfr.h" + +/* Backwards compatibility with mpfr<3.0.0 */ +#ifndef mpfr_exp_t +#define mpfr_exp_t mp_exp_t +#endif + +/* Define MPC version number */ +#define MPC_VERSION_MAJOR 1 +#define MPC_VERSION_MINOR 0 +#define MPC_VERSION_PATCHLEVEL 3 +#define MPC_VERSION_STRING "1.0.3" + +/* Macros dealing with MPC VERSION */ +#define MPC_VERSION_NUM(a,b,c) (((a) << 16L) | ((b) << 8) | (c)) +#define MPC_VERSION \ + MPC_VERSION_NUM(MPC_VERSION_MAJOR,MPC_VERSION_MINOR,MPC_VERSION_PATCHLEVEL) + +/* Check if stdint.h/inttypes.h is included */ +#if defined (INTMAX_C) && defined (UINTMAX_C) +#define _MPC_H_HAVE_INTMAX_T 1 +#endif + +/* Return values */ + +/* Transform negative to 2, positive to 1, leave 0 unchanged */ +#define MPC_INEX_POS(inex) (((inex) < 0) ? 2 : ((inex) == 0) ? 0 : 1) +/* Transform 2 to negative, 1 to positive, leave 0 unchanged */ +#define MPC_INEX_NEG(inex) (((inex) == 2) ? -1 : ((inex) == 0) ? 0 : 1) + +/* The global inexact flag is made of (real flag) + 4 * (imaginary flag), where + each of the real and imaginary inexact flag are: + 0 when the result is exact (no rounding error) + 1 when the result is larger than the exact value + 2 when the result is smaller than the exact value */ +#define MPC_INEX(inex_re, inex_im) \ + (MPC_INEX_POS(inex_re) | (MPC_INEX_POS(inex_im) << 2)) +#define MPC_INEX_RE(inex) MPC_INEX_NEG((inex) & 3) +#define MPC_INEX_IM(inex) MPC_INEX_NEG((inex) >> 2) + +/* For functions computing two results, the return value is + inexact1+16*inexact2, which is 0 iif both results are exact. */ +#define MPC_INEX12(inex1, inex2) (inex1 | (inex2 << 4)) +#define MPC_INEX1(inex) (inex & 15) +#define MPC_INEX2(inex) (inex >> 4) + +/* Definition of rounding modes */ + +/* a complex rounding mode is just a pair of two real rounding modes + we reserve four bits for a real rounding mode. */ +typedef int mpc_rnd_t; + +#define MPC_RND(r1,r2) (((int)(r1)) + ((int)(r2) << 4)) +#define MPC_RND_RE(x) ((mpfr_rnd_t)((x) & 0x0F)) +#define MPC_RND_IM(x) ((mpfr_rnd_t)((x) >> 4)) + +#define MPC_RNDNN MPC_RND (GMP_RNDN,GMP_RNDN) +#define MPC_RNDNZ MPC_RND (GMP_RNDN,GMP_RNDZ) +#define MPC_RNDNU MPC_RND (GMP_RNDN,GMP_RNDU) +#define MPC_RNDND MPC_RND (GMP_RNDN,GMP_RNDD) + +#define MPC_RNDZN MPC_RND (GMP_RNDZ,GMP_RNDN) +#define MPC_RNDZZ MPC_RND (GMP_RNDZ,GMP_RNDZ) +#define MPC_RNDZU MPC_RND (GMP_RNDZ,GMP_RNDU) +#define MPC_RNDZD MPC_RND (GMP_RNDZ,GMP_RNDD) + +#define MPC_RNDUN MPC_RND (GMP_RNDU,GMP_RNDN) +#define MPC_RNDUZ MPC_RND (GMP_RNDU,GMP_RNDZ) +#define MPC_RNDUU MPC_RND (GMP_RNDU,GMP_RNDU) +#define MPC_RNDUD MPC_RND (GMP_RNDU,GMP_RNDD) + +#define MPC_RNDDN MPC_RND (GMP_RNDD,GMP_RNDN) +#define MPC_RNDDZ MPC_RND (GMP_RNDD,GMP_RNDZ) +#define MPC_RNDDU MPC_RND (GMP_RNDD,GMP_RNDU) +#define MPC_RNDDD MPC_RND (GMP_RNDD,GMP_RNDD) + + +/* Definitions of types and their semantics */ + +typedef struct { + mpfr_t re; + mpfr_t im; +} +__mpc_struct; + +typedef __mpc_struct mpc_t[1]; +typedef __mpc_struct *mpc_ptr; +typedef const __mpc_struct *mpc_srcptr; + +/* Support for WINDOWS DLL, see + http://lists.gforge.inria.fr/pipermail/mpc-discuss/2011-November/000990.html; + when building the DLL, export symbols, otherwise behave as GMP */ +#if defined (__MPC_LIBRARY_BUILD) && __GMP_LIBGMP_DLL +#define __MPC_DECLSPEC __GMP_DECLSPEC_EXPORT +#else +#define __MPC_DECLSPEC __GMP_DECLSPEC +#endif + +#if defined (__cplusplus) +extern "C" { +#endif + +__MPC_DECLSPEC int mpc_add (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_add_fr (mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_add_si (mpc_ptr, mpc_srcptr, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_add_ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sub (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sub_fr (mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_fr_sub (mpc_ptr, mpfr_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sub_ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_ui_ui_sub (mpc_ptr, unsigned long int, unsigned long int, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_fr (mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_si (mpc_ptr, mpc_srcptr, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_i (mpc_ptr, mpc_srcptr, int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sqr (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_div (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_fr (mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_ld (mpc_ptr, mpc_srcptr, long double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_d (mpc_ptr, mpc_srcptr, double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_si (mpc_ptr, mpc_srcptr, long, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_ui (mpc_ptr, mpc_srcptr, unsigned long, mpc_rnd_t); +__MPC_DECLSPEC int mpc_pow_z (mpc_ptr, mpc_srcptr, mpz_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_div_fr (mpc_ptr, mpc_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_fr_div (mpc_ptr, mpfr_srcptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_div_ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_ui_div (mpc_ptr, unsigned long int, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_div_2ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_2ui (mpc_ptr, mpc_srcptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_div_2si (mpc_ptr, mpc_srcptr, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_mul_2si (mpc_ptr, mpc_srcptr, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_conj (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_neg (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_norm (mpfr_ptr, mpc_srcptr, mpfr_rnd_t); +__MPC_DECLSPEC int mpc_abs (mpfr_ptr, mpc_srcptr, mpfr_rnd_t); +__MPC_DECLSPEC int mpc_sqrt (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_d (mpc_ptr, double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_d_d (mpc_ptr, double, double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_ld (mpc_ptr, long double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_ld_ld (mpc_ptr, long double, long double, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_f (mpc_ptr, mpf_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_f_f (mpc_ptr, mpf_srcptr, mpf_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_fr (mpc_ptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_fr_fr (mpc_ptr, mpfr_srcptr, mpfr_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_q (mpc_ptr, mpq_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_q_q (mpc_ptr, mpq_srcptr, mpq_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_si (mpc_ptr, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_si_si (mpc_ptr, long int, long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_ui (mpc_ptr, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_ui_ui (mpc_ptr, unsigned long int, unsigned long int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_z (mpc_ptr, mpz_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_z_z (mpc_ptr, mpz_srcptr, mpz_srcptr, mpc_rnd_t); +__MPC_DECLSPEC void mpc_swap (mpc_ptr, mpc_ptr); +__MPC_DECLSPEC int mpc_fma (mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t); + +__MPC_DECLSPEC void mpc_set_nan (mpc_ptr); + +__MPC_DECLSPEC int mpc_real (mpfr_ptr, mpc_srcptr, mpfr_rnd_t); +__MPC_DECLSPEC int mpc_imag (mpfr_ptr, mpc_srcptr, mpfr_rnd_t); +__MPC_DECLSPEC int mpc_arg (mpfr_ptr, mpc_srcptr, mpfr_rnd_t); +__MPC_DECLSPEC int mpc_proj (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_cmp (mpc_srcptr, mpc_srcptr); +__MPC_DECLSPEC int mpc_cmp_si_si (mpc_srcptr, long int, long int); +__MPC_DECLSPEC int mpc_exp (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_log (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_log10 (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sin (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_cos (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sin_cos (mpc_ptr, mpc_ptr, mpc_srcptr, mpc_rnd_t, mpc_rnd_t); +__MPC_DECLSPEC int mpc_tan (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_sinh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_cosh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_tanh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_asin (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_acos (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_atan (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_asinh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_acosh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC int mpc_atanh (mpc_ptr, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC void mpc_clear (mpc_ptr); +__MPC_DECLSPEC int mpc_urandom (mpc_ptr, gmp_randstate_t); +__MPC_DECLSPEC void mpc_init2 (mpc_ptr, mpfr_prec_t); +__MPC_DECLSPEC void mpc_init3 (mpc_ptr, mpfr_prec_t, mpfr_prec_t); +__MPC_DECLSPEC mpfr_prec_t mpc_get_prec (mpc_srcptr x); +__MPC_DECLSPEC void mpc_get_prec2 (mpfr_prec_t *pr, mpfr_prec_t *pi, mpc_srcptr x); +__MPC_DECLSPEC void mpc_set_prec (mpc_ptr, mpfr_prec_t); +__MPC_DECLSPEC const char * mpc_get_version (void); + +__MPC_DECLSPEC int mpc_strtoc (mpc_ptr, const char *, char **, int, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_str (mpc_ptr, const char *, int, mpc_rnd_t); +__MPC_DECLSPEC char * mpc_get_str (int, size_t, mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC void mpc_free_str (char *); + +/* declare certain functions only if appropriate headers have been included */ +#ifdef _MPC_H_HAVE_INTMAX_T +__MPC_DECLSPEC int mpc_set_sj (mpc_ptr, intmax_t, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_uj (mpc_ptr, uintmax_t, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_sj_sj (mpc_ptr, intmax_t, intmax_t, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_uj_uj (mpc_ptr, uintmax_t, uintmax_t, mpc_rnd_t); +#endif + +#ifdef _Complex_I +__MPC_DECLSPEC int mpc_set_dc (mpc_ptr, double _Complex, mpc_rnd_t); +__MPC_DECLSPEC int mpc_set_ldc (mpc_ptr, long double _Complex, mpc_rnd_t); +__MPC_DECLSPEC double _Complex mpc_get_dc (mpc_srcptr, mpc_rnd_t); +__MPC_DECLSPEC long double _Complex mpc_get_ldc (mpc_srcptr, mpc_rnd_t); +#endif + +#ifdef _GMP_H_HAVE_FILE +__MPC_DECLSPEC int mpc_inp_str (mpc_ptr, FILE *, size_t *, int, mpc_rnd_t); +__MPC_DECLSPEC size_t mpc_out_str (FILE *, int, size_t, mpc_srcptr, mpc_rnd_t); +#endif + +#if defined (__cplusplus) +} +#endif + +#define mpc_realref(x) ((x)->re) +#define mpc_imagref(x) ((x)->im) + +#define mpc_cmp_si(x, y) \ + ( mpc_cmp_si_si ((x), (y), 0l) ) +#define mpc_ui_sub(x, y, z, r) mpc_ui_ui_sub (x, y, 0ul, z, r) + +/* + Define a fake mpfr_set_fr so that, for instance, mpc_set_fr_z would + be defined as follows: + mpc_set_fr_z (mpc_t rop, mpfr_t x, mpz_t y, mpc_rnd_t rnd) + MPC_SET_X_Y (fr, z, rop, x, y, rnd) +*/ +#ifndef mpfr_set_fr +#define mpfr_set_fr mpfr_set +#endif +#define MPC_SET_X_Y(real_t, imag_t, z, real_value, imag_value, rnd) \ + { \ + int _inex_re, _inex_im; \ + _inex_re = (mpfr_set_ ## real_t) (mpc_realref (z), (real_value), MPC_RND_RE (rnd)); \ + _inex_im = (mpfr_set_ ## imag_t) (mpc_imagref (z), (imag_value), MPC_RND_IM (rnd)); \ + return MPC_INEX (_inex_re, _inex_im); \ + } + +#endif /* ifndef __MPC_H */ diff --git a/mpc/src/mul.c b/mpc/src/mul.c new file mode 100644 index 0000000000..2be9b8d646 --- /dev/null +++ b/mpc/src/mul.c @@ -0,0 +1,639 @@ +/* mpc_mul -- Multiply two complex numbers + +Copyright (C) 2002, 2004, 2005, 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + +#define mpz_add_si(z,x,y) do { \ + if (y >= 0) \ + mpz_add_ui (z, x, (long int) y); \ + else \ + mpz_sub_ui (z, x, (long int) (-y)); \ + } while (0); + +/* compute z=x*y when x has an infinite part */ +static int +mul_infinite (mpc_ptr z, mpc_srcptr x, mpc_srcptr y) +{ + /* Let x=xr+i*xi and y=yr+i*yi; extract the signs of the operands */ + int xrs = mpfr_signbit (mpc_realref (x)) ? -1 : 1; + int xis = mpfr_signbit (mpc_imagref (x)) ? -1 : 1; + int yrs = mpfr_signbit (mpc_realref (y)) ? -1 : 1; + int yis = mpfr_signbit (mpc_imagref (y)) ? -1 : 1; + + int u, v; + + /* compute the sign of + u = xrs * yrs * xr * yr - xis * yis * xi * yi + v = xrs * yis * xr * yi + xis * yrs * xi * yr + +1 if positive, -1 if negatiye, 0 if NaN */ + if ( mpfr_nan_p (mpc_realref (x)) || mpfr_nan_p (mpc_imagref (x)) + || mpfr_nan_p (mpc_realref (y)) || mpfr_nan_p (mpc_imagref (y))) { + u = 0; + v = 0; + } + else if (mpfr_inf_p (mpc_realref (x))) { + /* x = (+/-inf) xr + i*xi */ + u = ( mpfr_zero_p (mpc_realref (y)) + || (mpfr_inf_p (mpc_imagref (x)) && mpfr_zero_p (mpc_imagref (y))) + || (mpfr_zero_p (mpc_imagref (x)) && mpfr_inf_p (mpc_imagref (y))) + || ( (mpfr_inf_p (mpc_imagref (x)) || mpfr_inf_p (mpc_imagref (y))) + && xrs*yrs == xis*yis) + ? 0 : xrs * yrs); + v = ( mpfr_zero_p (mpc_imagref (y)) + || (mpfr_inf_p (mpc_imagref (x)) && mpfr_zero_p (mpc_realref (y))) + || (mpfr_zero_p (mpc_imagref (x)) && mpfr_inf_p (mpc_realref (y))) + || ( (mpfr_inf_p (mpc_imagref (x)) || mpfr_inf_p (mpc_imagref (x))) + && xrs*yis != xis*yrs) + ? 0 : xrs * yis); + } + else { + /* x = xr + i*(+/-inf) with |xr| != inf */ + u = ( mpfr_zero_p (mpc_imagref (y)) + || (mpfr_zero_p (mpc_realref (x)) && mpfr_inf_p (mpc_realref (y))) + || (mpfr_inf_p (mpc_realref (y)) && xrs*yrs == xis*yis) + ? 0 : -xis * yis); + v = ( mpfr_zero_p (mpc_realref (y)) + || (mpfr_zero_p (mpc_realref (x)) && mpfr_inf_p (mpc_imagref (y))) + || (mpfr_inf_p (mpc_imagref (y)) && xrs*yis != xis*yrs) + ? 0 : xis * yrs); + } + + if (u == 0 && v == 0) { + /* Naive result is NaN+i*NaN. Obtain an infinity using the algorithm + given in Annex G.5.1 of the ISO C99 standard */ + int xr = (mpfr_zero_p (mpc_realref (x)) || mpfr_nan_p (mpc_realref (x)) ? 0 + : (mpfr_inf_p (mpc_realref (x)) ? 1 : 0)); + int xi = (mpfr_zero_p (mpc_imagref (x)) || mpfr_nan_p (mpc_imagref (x)) ? 0 + : (mpfr_inf_p (mpc_imagref (x)) ? 1 : 0)); + int yr = (mpfr_zero_p (mpc_realref (y)) || mpfr_nan_p (mpc_realref (y)) ? 0 : 1); + int yi = (mpfr_zero_p (mpc_imagref (y)) || mpfr_nan_p (mpc_imagref (y)) ? 0 : 1); + if (mpc_inf_p (y)) { + yr = mpfr_inf_p (mpc_realref (y)) ? 1 : 0; + yi = mpfr_inf_p (mpc_imagref (y)) ? 1 : 0; + } + + u = xrs * xr * yrs * yr - xis * xi * yis * yi; + v = xrs * xr * yis * yi + xis * xi * yrs * yr; + } + + if (u == 0) + mpfr_set_nan (mpc_realref (z)); + else + mpfr_set_inf (mpc_realref (z), u); + + if (v == 0) + mpfr_set_nan (mpc_imagref (z)); + else + mpfr_set_inf (mpc_imagref (z), v); + + return MPC_INEX (0, 0); /* exact */ +} + + +/* compute z = x*y for Im(y) == 0 */ +static int +mul_real (mpc_ptr z, mpc_srcptr x, mpc_srcptr y, mpc_rnd_t rnd) +{ + int xrs, xis, yrs, yis; + int inex; + + /* save signs of operands */ + xrs = MPFR_SIGNBIT (mpc_realref (x)); + xis = MPFR_SIGNBIT (mpc_imagref (x)); + yrs = MPFR_SIGNBIT (mpc_realref (y)); + yis = MPFR_SIGNBIT (mpc_imagref (y)); + + inex = mpc_mul_fr (z, x, mpc_realref (y), rnd); + /* Signs of zeroes may be wrong. Their correction does not change the + inexact flag. */ + if (mpfr_zero_p (mpc_realref (z))) + mpfr_setsign (mpc_realref (z), mpc_realref (z), MPC_RND_RE(rnd) == GMP_RNDD + || (xrs != yrs && xis == yis), GMP_RNDN); + if (mpfr_zero_p (mpc_imagref (z))) + mpfr_setsign (mpc_imagref (z), mpc_imagref (z), MPC_RND_IM (rnd) == GMP_RNDD + || (xrs != yis && xis != yrs), GMP_RNDN); + + return inex; +} + + +/* compute z = x*y for Re(y) == 0, and Im(x) != 0 and Im(y) != 0 */ +static int +mul_imag (mpc_ptr z, mpc_srcptr x, mpc_srcptr y, mpc_rnd_t rnd) +{ + int sign; + int inex_re, inex_im; + int overlap = z == x || z == y; + mpc_t rop; + + if (overlap) + mpc_init3 (rop, MPC_PREC_RE (z), MPC_PREC_IM (z)); + else + rop [0] = z[0]; + + sign = (MPFR_SIGNBIT (mpc_realref (y)) != MPFR_SIGNBIT (mpc_imagref (x))) + && (MPFR_SIGNBIT (mpc_imagref (y)) != MPFR_SIGNBIT (mpc_realref (x))); + + inex_re = -mpfr_mul (mpc_realref (rop), mpc_imagref (x), mpc_imagref (y), + INV_RND (MPC_RND_RE (rnd))); + mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN); /* exact */ + inex_im = mpfr_mul (mpc_imagref (rop), mpc_realref (x), mpc_imagref (y), + MPC_RND_IM (rnd)); + mpc_set (z, rop, MPC_RNDNN); + + /* Sign of zeroes may be wrong (note that Re(z) cannot be zero) */ + if (mpfr_zero_p (mpc_imagref (z))) + mpfr_setsign (mpc_imagref (z), mpc_imagref (z), MPC_RND_IM (rnd) == GMP_RNDD + || sign, GMP_RNDN); + + if (overlap) + mpc_clear (rop); + + return MPC_INEX (inex_re, inex_im); +} + + +static int +mpfr_fmma (mpfr_ptr z, mpfr_srcptr a, mpfr_srcptr b, mpfr_srcptr c, + mpfr_srcptr d, int sign, mpfr_rnd_t rnd) +{ + /* Computes z = ab+cd if sign >= 0, or z = ab-cd if sign < 0. + Assumes that a, b, c, d are finite and non-zero; so any multiplication + of two of them yielding an infinity is an overflow, and a + multiplication yielding 0 is an underflow. + Assumes further that z is distinct from a, b, c, d. */ + + int inex; + mpfr_t u, v; + + /* u=a*b, v=sign*c*d exactly */ + mpfr_init2 (u, mpfr_get_prec (a) + mpfr_get_prec (b)); + mpfr_init2 (v, mpfr_get_prec (c) + mpfr_get_prec (d)); + mpfr_mul (u, a, b, GMP_RNDN); + mpfr_mul (v, c, d, GMP_RNDN); + if (sign < 0) + mpfr_neg (v, v, GMP_RNDN); + + /* tentatively compute z as u+v; here we need z to be distinct + from a, b, c, d to not lose the latter */ + inex = mpfr_add (z, u, v, rnd); + + if (mpfr_inf_p (z)) { + /* replace by "correctly rounded overflow" */ + mpfr_set_si (z, (mpfr_signbit (z) ? -1 : 1), GMP_RNDN); + inex = mpfr_mul_2ui (z, z, mpfr_get_emax (), rnd); + } + else if (mpfr_zero_p (u) && !mpfr_zero_p (v)) { + /* exactly u underflowed, determine inexact flag */ + inex = (mpfr_signbit (u) ? 1 : -1); + } + else if (mpfr_zero_p (v) && !mpfr_zero_p (u)) { + /* exactly v underflowed, determine inexact flag */ + inex = (mpfr_signbit (v) ? 1 : -1); + } + else if (mpfr_nan_p (z) || (mpfr_zero_p (u) && mpfr_zero_p (v))) { + /* In the first case, u and v are infinities with opposite signs. + In the second case, u and v are zeroes; their sum may be 0 or the + least representable number, with a sign to be determined. + Redo the computations with mpz_t exponents */ + mpfr_exp_t ea, eb, ec, ed; + mpz_t eu, ev; + /* cheat to work around the const qualifiers */ + + /* Normalise the input by shifting and keep track of the shifts in + the exponents of u and v */ + ea = mpfr_get_exp (a); + eb = mpfr_get_exp (b); + ec = mpfr_get_exp (c); + ed = mpfr_get_exp (d); + + mpfr_set_exp ((mpfr_ptr) a, (mpfr_prec_t) 0); + mpfr_set_exp ((mpfr_ptr) b, (mpfr_prec_t) 0); + mpfr_set_exp ((mpfr_ptr) c, (mpfr_prec_t) 0); + mpfr_set_exp ((mpfr_ptr) d, (mpfr_prec_t) 0); + + mpz_init (eu); + mpz_init (ev); + mpz_set_si (eu, (long int) ea); + mpz_add_si (eu, eu, (long int) eb); + mpz_set_si (ev, (long int) ec); + mpz_add_si (ev, ev, (long int) ed); + + /* recompute u and v and move exponents to eu and ev */ + mpfr_mul (u, a, b, GMP_RNDN); + /* exponent of u is non-positive */ + mpz_sub_ui (eu, eu, (unsigned long int) (-mpfr_get_exp (u))); + mpfr_set_exp (u, (mpfr_prec_t) 0); + mpfr_mul (v, c, d, GMP_RNDN); + if (sign < 0) + mpfr_neg (v, v, GMP_RNDN); + mpz_sub_ui (ev, ev, (unsigned long int) (-mpfr_get_exp (v))); + mpfr_set_exp (v, (mpfr_prec_t) 0); + + if (mpfr_nan_p (z)) { + mpfr_exp_t emax = mpfr_get_emax (); + int overflow; + /* We have a = ma * 2^ea with 1/2 <= |ma| < 1 and ea <= emax, and + analogously for b. So eu <= 2*emax, and eu > emax since we have + an overflow. The same holds for ev. Shift u and v by as much as + possible so that one of them has exponent emax and the + remaining exponents in eu and ev are the same. Then carry out + the addition. Shifting u and v prevents an underflow. */ + if (mpz_cmp (eu, ev) >= 0) { + mpfr_set_exp (u, emax); + mpz_sub_ui (eu, eu, (long int) emax); + mpz_sub (ev, ev, eu); + mpfr_set_exp (v, (mpfr_exp_t) mpz_get_ui (ev)); + /* remaining common exponent is now in eu */ + } + else { + mpfr_set_exp (v, emax); + mpz_sub_ui (ev, ev, (long int) emax); + mpz_sub (eu, eu, ev); + mpfr_set_exp (u, (mpfr_exp_t) mpz_get_ui (eu)); + mpz_set (eu, ev); + /* remaining common exponent is now also in eu */ + } + inex = mpfr_add (z, u, v, rnd); + /* Result is finite since u and v have different signs. */ + overflow = mpfr_mul_2ui (z, z, mpz_get_ui (eu), rnd); + if (overflow) + inex = overflow; + } + else { + int underflow; + /* Addition of two zeroes with same sign. We have a = ma * 2^ea + with 1/2 <= |ma| < 1 and ea >= emin and similarly for b. + So 2*emin < 2*emin+1 <= eu < emin < 0, and analogously for v. */ + mpfr_exp_t emin = mpfr_get_emin (); + if (mpz_cmp (eu, ev) <= 0) { + mpfr_set_exp (u, emin); + mpz_add_ui (eu, eu, (unsigned long int) (-emin)); + mpz_sub (ev, ev, eu); + mpfr_set_exp (v, (mpfr_exp_t) mpz_get_si (ev)); + } + else { + mpfr_set_exp (v, emin); + mpz_add_ui (ev, ev, (unsigned long int) (-emin)); + mpz_sub (eu, eu, ev); + mpfr_set_exp (u, (mpfr_exp_t) mpz_get_si (eu)); + mpz_set (eu, ev); + } + inex = mpfr_add (z, u, v, rnd); + mpz_neg (eu, eu); + underflow = mpfr_div_2ui (z, z, mpz_get_ui (eu), rnd); + if (underflow) + inex = underflow; + } + + mpz_clear (eu); + mpz_clear (ev); + + mpfr_set_exp ((mpfr_ptr) a, ea); + mpfr_set_exp ((mpfr_ptr) b, eb); + mpfr_set_exp ((mpfr_ptr) c, ec); + mpfr_set_exp ((mpfr_ptr) d, ed); + /* works also when some of a, b, c, d are not all distinct */ + } + + mpfr_clear (u); + mpfr_clear (v); + + return inex; +} + + +int +mpc_mul_naive (mpc_ptr z, mpc_srcptr x, mpc_srcptr y, mpc_rnd_t rnd) +{ + /* computes z=x*y by the schoolbook method, where x and y are assumed + to be finite and without zero parts */ + int overlap, inex; + mpc_t rop; + + MPC_ASSERT ( mpfr_regular_p (mpc_realref (x)) && mpfr_regular_p (mpc_imagref (x)) + && mpfr_regular_p (mpc_realref (y)) && mpfr_regular_p (mpc_imagref (y))); + overlap = (z == x) || (z == y); + if (overlap) + mpc_init3 (rop, MPC_PREC_RE (z), MPC_PREC_IM (z)); + else + rop [0] = z [0]; + + inex = MPC_INEX (mpfr_fmma (mpc_realref (rop), mpc_realref (x), mpc_realref (y), mpc_imagref (x), + mpc_imagref (y), -1, MPC_RND_RE (rnd)), + mpfr_fmma (mpc_imagref (rop), mpc_realref (x), mpc_imagref (y), mpc_imagref (x), + mpc_realref (y), +1, MPC_RND_IM (rnd))); + + mpc_set (z, rop, MPC_RNDNN); + if (overlap) + mpc_clear (rop); + + return inex; +} + + +int +mpc_mul_karatsuba (mpc_ptr rop, mpc_srcptr op1, mpc_srcptr op2, mpc_rnd_t rnd) +{ + /* computes rop=op1*op2 by a Karatsuba algorithm, where op1 and op2 + are assumed to be finite and without zero parts */ + mpfr_srcptr a, b, c, d; + int mul_i, ok, inexact, mul_a, mul_c, inex_re = 0, inex_im = 0, sign_x, sign_u; + mpfr_t u, v, w, x; + mpfr_prec_t prec, prec_re, prec_u, prec_v, prec_w; + mpfr_rnd_t rnd_re, rnd_u; + int overlap; + /* true if rop == op1 or rop == op2 */ + mpc_t result; + /* overlap is quite difficult to handle, because we have to tentatively + round the variable u in the end to either the real or the imaginary + part of rop (it is not possible to tell now whether the real or + imaginary part is used). If this fails, we have to start again and + need the correct values of op1 and op2. + So we just create a new variable for the result in this case. */ + int loop; + const int MAX_MUL_LOOP = 1; + + overlap = (rop == op1) || (rop == op2); + if (overlap) + mpc_init3 (result, MPC_PREC_RE (rop), MPC_PREC_IM (rop)); + else + result [0] = rop [0]; + + a = mpc_realref(op1); + b = mpc_imagref(op1); + c = mpc_realref(op2); + d = mpc_imagref(op2); + + /* (a + i*b) * (c + i*d) = [ac - bd] + i*[ad + bc] */ + + mul_i = 0; /* number of multiplications by i */ + mul_a = 1; /* implicit factor for a */ + mul_c = 1; /* implicit factor for c */ + + if (mpfr_cmp_abs (a, b) < 0) + { + MPFR_SWAP (a, b); + mul_i ++; + mul_a = -1; /* consider i * (a+i*b) = -b + i*a */ + } + + if (mpfr_cmp_abs (c, d) < 0) + { + MPFR_SWAP (c, d); + mul_i ++; + mul_c = -1; /* consider -d + i*c instead of c + i*d */ + } + + /* find the precision and rounding mode for the new real part */ + if (mul_i % 2) + { + prec_re = MPC_PREC_IM(rop); + rnd_re = MPC_RND_IM(rnd); + } + else /* mul_i = 0 or 2 */ + { + prec_re = MPC_PREC_RE(rop); + rnd_re = MPC_RND_RE(rnd); + } + + if (mul_i) + rnd_re = INV_RND(rnd_re); + + /* now |a| >= |b| and |c| >= |d| */ + prec = MPC_MAX_PREC(rop); + + mpfr_init2 (v, prec_v = mpfr_get_prec (a) + mpfr_get_prec (d)); + mpfr_init2 (w, prec_w = mpfr_get_prec (b) + mpfr_get_prec (c)); + mpfr_init2 (u, 2); + mpfr_init2 (x, 2); + + inexact = mpfr_mul (v, a, d, GMP_RNDN); + if (inexact) { + /* over- or underflow */ + ok = 0; + goto clear; + } + if (mul_a == -1) + mpfr_neg (v, v, GMP_RNDN); + + inexact = mpfr_mul (w, b, c, GMP_RNDN); + if (inexact) { + /* over- or underflow */ + ok = 0; + goto clear; + } + if (mul_c == -1) + mpfr_neg (w, w, GMP_RNDN); + + /* compute sign(v-w) */ + sign_x = mpfr_cmp_abs (v, w); + if (sign_x > 0) + sign_x = 2 * mpfr_sgn (v) - mpfr_sgn (w); + else if (sign_x == 0) + sign_x = mpfr_sgn (v) - mpfr_sgn (w); + else + sign_x = mpfr_sgn (v) - 2 * mpfr_sgn (w); + + sign_u = mul_a * mpfr_sgn (a) * mul_c * mpfr_sgn (c); + + if (sign_x * sign_u < 0) + { /* swap inputs */ + MPFR_SWAP (a, c); + MPFR_SWAP (b, d); + mpfr_swap (v, w); + { int tmp; tmp = mul_a; mul_a = mul_c; mul_c = tmp; } + sign_x = - sign_x; + } + + /* now sign_x * sign_u >= 0 */ + loop = 0; + do + { + loop++; + /* the following should give failures with prob. <= 1/prec */ + prec += mpc_ceil_log2 (prec) + 3; + + mpfr_set_prec (u, prec_u = prec); + mpfr_set_prec (x, prec); + + /* first compute away(b +/- a) and store it in u */ + inexact = (mul_a == -1 ? + ROUND_AWAY (mpfr_sub (u, b, a, MPFR_RNDA), u) : + ROUND_AWAY (mpfr_add (u, b, a, MPFR_RNDA), u)); + + /* then compute away(+/-c - d) and store it in x */ + inexact |= (mul_c == -1 ? + ROUND_AWAY (mpfr_add (x, c, d, MPFR_RNDA), x) : + ROUND_AWAY (mpfr_sub (x, c, d, MPFR_RNDA), x)); + if (mul_c == -1) + mpfr_neg (x, x, GMP_RNDN); + + if (inexact == 0) + mpfr_prec_round (u, prec_u = 2 * prec, GMP_RNDN); + + /* compute away(u*x) and store it in u */ + inexact |= ROUND_AWAY (mpfr_mul (u, u, x, MPFR_RNDA), u); + /* (a+b)*(c-d) */ + + /* if all computations are exact up to here, it may be that + the real part is exact, thus we need if possible to + compute v - w exactly */ + if (inexact == 0) + { + mpfr_prec_t prec_x; + /* v and w are different from 0, so mpfr_get_exp is safe to use */ + prec_x = SAFE_ABS (mpfr_exp_t, mpfr_get_exp (v) - mpfr_get_exp (w)) + + MPC_MAX (prec_v, prec_w) + 1; + /* +1 is necessary for a potential carry */ + /* ensure we do not use a too large precision */ + if (prec_x > prec_u) + prec_x = prec_u; + if (prec_x > prec) + mpfr_prec_round (x, prec_x, GMP_RNDN); + } + + rnd_u = (sign_u > 0) ? GMP_RNDU : GMP_RNDD; + inexact |= mpfr_sub (x, v, w, rnd_u); /* ad - bc */ + + /* in case u=0, ensure that rnd_u rounds x away from zero */ + if (mpfr_sgn (u) == 0) + rnd_u = (mpfr_sgn (x) > 0) ? GMP_RNDU : GMP_RNDD; + inexact |= mpfr_add (u, u, x, rnd_u); /* ac - bd */ + + ok = inexact == 0 || + mpfr_can_round (u, prec_u - 3, rnd_u, GMP_RNDZ, + prec_re + (rnd_re == GMP_RNDN)); + /* this ensures both we can round correctly and determine the correct + inexact flag (for rounding to nearest) */ + } + while (!ok && loop <= MAX_MUL_LOOP); + /* after MAX_MUL_LOOP rounds, use mpc_naive instead */ + + if (ok) { + /* if inexact is zero, then u is exactly ac-bd, otherwise fix the sign + of the inexact flag for u, which was rounded away from ac-bd */ + if (inexact != 0) + inexact = mpfr_sgn (u); + + if (mul_i == 0) + { + inex_re = mpfr_set (mpc_realref(result), u, MPC_RND_RE(rnd)); + if (inex_re == 0) + { + inex_re = inexact; /* u is rounded away from 0 */ + inex_im = mpfr_add (mpc_imagref(result), v, w, MPC_RND_IM(rnd)); + } + else + inex_im = mpfr_add (mpc_imagref(result), v, w, MPC_RND_IM(rnd)); + } + else if (mul_i == 1) /* (x+i*y)/i = y - i*x */ + { + inex_im = mpfr_neg (mpc_imagref(result), u, MPC_RND_IM(rnd)); + if (inex_im == 0) + { + inex_im = -inexact; /* u is rounded away from 0 */ + inex_re = mpfr_add (mpc_realref(result), v, w, MPC_RND_RE(rnd)); + } + else + inex_re = mpfr_add (mpc_realref(result), v, w, MPC_RND_RE(rnd)); + } + else /* mul_i = 2, z/i^2 = -z */ + { + inex_re = mpfr_neg (mpc_realref(result), u, MPC_RND_RE(rnd)); + if (inex_re == 0) + { + inex_re = -inexact; /* u is rounded away from 0 */ + inex_im = -mpfr_add (mpc_imagref(result), v, w, + INV_RND(MPC_RND_IM(rnd))); + mpfr_neg (mpc_imagref(result), mpc_imagref(result), MPC_RND_IM(rnd)); + } + else + { + inex_im = -mpfr_add (mpc_imagref(result), v, w, + INV_RND(MPC_RND_IM(rnd))); + mpfr_neg (mpc_imagref(result), mpc_imagref(result), MPC_RND_IM(rnd)); + } + } + + mpc_set (rop, result, MPC_RNDNN); + } + +clear: + mpfr_clear (u); + mpfr_clear (v); + mpfr_clear (w); + mpfr_clear (x); + if (overlap) + mpc_clear (result); + + if (ok) + return MPC_INEX(inex_re, inex_im); + else + return mpc_mul_naive (rop, op1, op2, rnd); +} + + +int +mpc_mul (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + /* Conforming to ISO C99 standard (G.5.1 multiplicative operators), + infinities are treated specially if both parts are NaN when computed + naively. */ + if (mpc_inf_p (b)) + return mul_infinite (a, b, c); + if (mpc_inf_p (c)) + return mul_infinite (a, c, b); + + /* NaN contamination of both parts in result */ + if (mpfr_nan_p (mpc_realref (b)) || mpfr_nan_p (mpc_imagref (b)) + || mpfr_nan_p (mpc_realref (c)) || mpfr_nan_p (mpc_imagref (c))) { + mpfr_set_nan (mpc_realref (a)); + mpfr_set_nan (mpc_imagref (a)); + return MPC_INEX (0, 0); + } + + /* check for real multiplication */ + if (mpfr_zero_p (mpc_imagref (b))) + return mul_real (a, c, b, rnd); + if (mpfr_zero_p (mpc_imagref (c))) + return mul_real (a, b, c, rnd); + + /* check for purely imaginary multiplication */ + if (mpfr_zero_p (mpc_realref (b))) + return mul_imag (a, c, b, rnd); + if (mpfr_zero_p (mpc_realref (c))) + return mul_imag (a, b, c, rnd); + + /* If the real and imaginary part of one argument have a very different */ + /* exponent, it is not reasonable to use Karatsuba multiplication. */ + if ( SAFE_ABS (mpfr_exp_t, + mpfr_get_exp (mpc_realref (b)) - mpfr_get_exp (mpc_imagref (b))) + > (mpfr_exp_t) MPC_MAX_PREC (b) / 2 + || SAFE_ABS (mpfr_exp_t, + mpfr_get_exp (mpc_realref (c)) - mpfr_get_exp (mpc_imagref (c))) + > (mpfr_exp_t) MPC_MAX_PREC (c) / 2) + return mpc_mul_naive (a, b, c, rnd); + else + return ((MPC_MAX_PREC(a) + <= (mpfr_prec_t) MUL_KARATSUBA_THRESHOLD * BITS_PER_MP_LIMB) + ? mpc_mul_naive : mpc_mul_karatsuba) (a, b, c, rnd); +} diff --git a/mpc/src/mul_2si.c b/mpc/src/mul_2si.c new file mode 100644 index 0000000000..14d0ca2516 --- /dev/null +++ b/mpc/src/mul_2si.c @@ -0,0 +1,32 @@ +/* mpc_mul_2si -- Multiply a complex number by 2^e. + +Copyright (C) 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_2si (mpc_ptr a, mpc_srcptr b, long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_mul_2si (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_mul_2si (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/mul_2ui.c b/mpc/src/mul_2ui.c new file mode 100644 index 0000000000..46aa788ec9 --- /dev/null +++ b/mpc/src/mul_2ui.c @@ -0,0 +1,32 @@ +/* mpc_mul_2ui -- Multiply a complex number by 2^e. + +Copyright (C) 2002, 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_2ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_mul_2ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_mul_2ui (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/mul_fr.c b/mpc/src/mul_fr.c new file mode 100644 index 0000000000..bd3574d857 --- /dev/null +++ b/mpc/src/mul_fr.c @@ -0,0 +1,43 @@ +/* mpc_mul_fr -- Multiply a complex number by a floating-point number. + +Copyright (C) 2002, 2008, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_fr (mpc_ptr a, mpc_srcptr b, mpfr_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + mpfr_t real; + + if (c == mpc_realref (a)) + /* We have to use a temporary variable. */ + mpfr_init2 (real, MPC_PREC_RE (a)); + else + real [0] = mpc_realref (a) [0]; + + inex_re = mpfr_mul (real, mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_mul (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + mpfr_set (mpc_realref (a), real, GMP_RNDN); /* exact */ + + if (c == mpc_realref (a)) + mpfr_clear (real); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/mul_i.c b/mpc/src/mul_i.c new file mode 100644 index 0000000000..591b0c6d7f --- /dev/null +++ b/mpc/src/mul_i.c @@ -0,0 +1,80 @@ +/* mpc_mul_i -- Multiply a complex number by plus or minus i. + +Copyright (C) 2005, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_i (mpc_ptr a, mpc_srcptr b, int sign, mpc_rnd_t rnd) +/* if sign is >= 0, multiply by i, otherwise by -i */ +{ + int inex_re, inex_im; + mpfr_t tmp; + + /* Treat the most probable case of compatible precisions first */ + if ( MPC_PREC_RE (b) == MPC_PREC_IM (a) + && MPC_PREC_IM (b) == MPC_PREC_RE (a)) + { + if (a == b) + mpfr_swap (mpc_realref (a), mpc_imagref (a)); + else + { + mpfr_set (mpc_realref (a), mpc_imagref (b), GMP_RNDN); + mpfr_set (mpc_imagref (a), mpc_realref (b), GMP_RNDN); + } + if (sign >= 0) + MPFR_CHANGE_SIGN (mpc_realref (a)); + else + MPFR_CHANGE_SIGN (mpc_imagref (a)); + inex_re = 0; + inex_im = 0; + } + else + { + if (a == b) + { + mpfr_init2 (tmp, MPC_PREC_RE (a)); + if (sign >= 0) + { + inex_re = mpfr_neg (tmp, mpc_imagref (b), MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (a), mpc_realref (b), MPC_RND_IM (rnd)); + } + else + { + inex_re = mpfr_set (tmp, mpc_imagref (b), MPC_RND_RE (rnd)); + inex_im = mpfr_neg (mpc_imagref (a), mpc_realref (b), MPC_RND_IM (rnd)); + } + mpfr_clear (mpc_realref (a)); + mpc_realref (a)[0] = tmp [0]; + } + else + if (sign >= 0) + { + inex_re = mpfr_neg (mpc_realref (a), mpc_imagref (b), MPC_RND_RE (rnd)); + inex_im = mpfr_set (mpc_imagref (a), mpc_realref (b), MPC_RND_IM (rnd)); + } + else + { + inex_re = mpfr_set (mpc_realref (a), mpc_imagref (b), MPC_RND_RE (rnd)); + inex_im = mpfr_neg (mpc_imagref (a), mpc_realref (b), MPC_RND_IM (rnd)); + } + } + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/mul_si.c b/mpc/src/mul_si.c new file mode 100644 index 0000000000..f539d8b075 --- /dev/null +++ b/mpc/src/mul_si.c @@ -0,0 +1,32 @@ +/* mpc_mul_si -- Multiply a complex number by a signed integer. + +Copyright (C) 2005, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_si (mpc_ptr a, mpc_srcptr b, long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_mul_si (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_mul_si (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/mul_ui.c b/mpc/src/mul_ui.c new file mode 100644 index 0000000000..922e4b3777 --- /dev/null +++ b/mpc/src/mul_ui.c @@ -0,0 +1,32 @@ +/* mpc_mul_ui -- Multiply a complex number by a nonnegative integer. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_mul_ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_mul_ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_mul_ui (mpc_imagref(a), mpc_imagref(b), c, MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/neg.c b/mpc/src/neg.c new file mode 100644 index 0000000000..2aae7ca4db --- /dev/null +++ b/mpc/src/neg.c @@ -0,0 +1,32 @@ +/* mpc_neg -- Negate a complex number. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_neg (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_neg (mpc_realref(a), mpc_realref(b), MPC_RND_RE(rnd)); + inex_im = mpfr_neg (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/norm.c b/mpc/src/norm.c new file mode 100644 index 0000000000..ab413b6798 --- /dev/null +++ b/mpc/src/norm.c @@ -0,0 +1,182 @@ +/* mpc_norm -- Square of the norm of a complex number. + +Copyright (C) 2002, 2005, 2008, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + +/* a <- norm(b) = b * conj(b) + (the rounding mode is mpfr_rnd_t here since we return an mpfr number) */ +int +mpc_norm (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) +{ + int inexact; + int saved_underflow, saved_overflow; + + /* handling of special values; consistent with abs in that + norm = abs^2; so norm (+-inf, xxx) = norm (xxx, +-inf) = +inf */ + if (!mpc_fin_p (b)) + return mpc_abs (a, b, rnd); + else if (mpfr_zero_p (mpc_realref (b))) { + if (mpfr_zero_p (mpc_imagref (b))) + return mpfr_set_ui (a, 0, rnd); /* +0 */ + else + return mpfr_sqr (a, mpc_imagref (b), rnd); + } + else if (mpfr_zero_p (mpc_imagref (b))) + return mpfr_sqr (a, mpc_realref (b), rnd); /* Re(b) <> 0 */ + + else /* everything finite and non-zero */ { + mpfr_t u, v, res; + mpfr_prec_t prec, prec_u, prec_v; + int loops; + const int max_loops = 2; + /* switch to exact squarings when loops==max_loops */ + + prec = mpfr_get_prec (a); + + mpfr_init (u); + mpfr_init (v); + mpfr_init (res); + + /* save the underflow or overflow flags from MPFR */ + saved_underflow = mpfr_underflow_p (); + saved_overflow = mpfr_overflow_p (); + + loops = 0; + mpfr_clear_underflow (); + mpfr_clear_overflow (); + do { + loops++; + prec += mpc_ceil_log2 (prec) + 3; + if (loops >= max_loops) { + prec_u = 2 * MPC_PREC_RE (b); + prec_v = 2 * MPC_PREC_IM (b); + } + else { + prec_u = MPC_MIN (prec, 2 * MPC_PREC_RE (b)); + prec_v = MPC_MIN (prec, 2 * MPC_PREC_IM (b)); + } + + mpfr_set_prec (u, prec_u); + mpfr_set_prec (v, prec_v); + + inexact = mpfr_sqr (u, mpc_realref(b), GMP_RNDD); /* err <= 1 ulp in prec */ + inexact |= mpfr_sqr (v, mpc_imagref(b), GMP_RNDD); /* err <= 1 ulp in prec */ + + /* If loops = max_loops, inexact should be 0 here, except in case + of underflow or overflow. + If loops < max_loops and inexact is zero, we can exit the + while-loop since it only remains to add u and v into a. */ + if (inexact) { + mpfr_set_prec (res, prec); + mpfr_add (res, u, v, GMP_RNDD); /* err <= 3 ulp in prec */ + } + + } while (loops < max_loops && inexact != 0 + && !mpfr_can_round (res, prec - 2, GMP_RNDD, GMP_RNDU, + mpfr_get_prec (a) + (rnd == GMP_RNDN))); + + if (!inexact) + /* squarings were exact, neither underflow nor overflow */ + inexact = mpfr_add (a, u, v, rnd); + /* if there was an overflow in Re(b)^2 or Im(b)^2 or their sum, + since the norm is larger, there is an overflow for the norm */ + else if (mpfr_overflow_p ()) { + /* replace by "correctly rounded overflow" */ + mpfr_set_ui (a, 1ul, GMP_RNDN); + inexact = mpfr_mul_2ui (a, a, mpfr_get_emax (), rnd); + } + else if (mpfr_underflow_p ()) { + /* necessarily one of the squarings did underflow (otherwise their + sum could not underflow), thus one of u, v is zero. */ + mpfr_exp_t emin = mpfr_get_emin (); + + /* Now either both u and v are zero, or u is zero and v exact, + or v is zero and u exact. + In the latter case, Im(b)^2 < 2^(emin-1). + If ulp(u) >= 2^(emin+1) and norm(b) is not exactly + representable at the target precision, then rounding u+Im(b)^2 + is equivalent to rounding u+2^(emin-1). + For instance, if exp(u)>0 and the target precision is smaller + than about |emin|, the norm is not representable. To make the + scaling in the "else" case work without underflow, we test + whether exp(u) is larger than a small negative number instead. + The second case is handled analogously. */ + if (!mpfr_zero_p (u) + && mpfr_get_exp (u) - 2 * (mpfr_exp_t) prec_u > emin + && mpfr_get_exp (u) > -10) { + mpfr_set_prec (v, MPFR_PREC_MIN); + mpfr_set_ui_2exp (v, 1, emin - 1, GMP_RNDZ); + inexact = mpfr_add (a, u, v, rnd); + } + else if (!mpfr_zero_p (v) + && mpfr_get_exp (v) - 2 * (mpfr_exp_t) prec_v > emin + && mpfr_get_exp (v) > -10) { + mpfr_set_prec (u, MPFR_PREC_MIN); + mpfr_set_ui_2exp (u, 1, emin - 1, GMP_RNDZ); + inexact = mpfr_add (a, u, v, rnd); + } + else { + unsigned long int scale, exp_re, exp_im; + int inex_underflow; + + /* scale the input to an average exponent close to 0 */ + exp_re = (unsigned long int) (-mpfr_get_exp (mpc_realref (b))); + exp_im = (unsigned long int) (-mpfr_get_exp (mpc_imagref (b))); + scale = exp_re / 2 + exp_im / 2 + (exp_re % 2 + exp_im % 2) / 2; + /* (exp_re + exp_im) / 2, computed in a way avoiding + integer overflow */ + if (mpfr_zero_p (u)) { + /* recompute the scaled value exactly */ + mpfr_mul_2ui (u, mpc_realref (b), scale, GMP_RNDN); + mpfr_sqr (u, u, GMP_RNDN); + } + else /* just scale */ + mpfr_mul_2ui (u, u, 2*scale, GMP_RNDN); + if (mpfr_zero_p (v)) { + mpfr_mul_2ui (v, mpc_imagref (b), scale, GMP_RNDN); + mpfr_sqr (v, v, GMP_RNDN); + } + else + mpfr_mul_2ui (v, v, 2*scale, GMP_RNDN); + + inexact = mpfr_add (a, u, v, rnd); + mpfr_clear_underflow (); + inex_underflow = mpfr_div_2ui (a, a, 2*scale, rnd); + if (mpfr_underflow_p ()) + inexact = inex_underflow; + } + } + else /* no problems, ternary value due to mpfr_can_round trick */ + inexact = mpfr_set (a, res, rnd); + + /* restore underflow and overflow flags from MPFR */ + if (saved_underflow) + mpfr_set_underflow (); + if (saved_overflow) + mpfr_set_overflow (); + + mpfr_clear (u); + mpfr_clear (v); + mpfr_clear (res); + } + + return inexact; +} diff --git a/mpc/src/out_str.c b/mpc/src/out_str.c new file mode 100644 index 0000000000..87cc823308 --- /dev/null +++ b/mpc/src/out_str.c @@ -0,0 +1,39 @@ +/* mpc_out_str -- Output a complex number on a given stream. + +Copyright (C) 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for FILE */ +#include <ctype.h> +#include "mpc-impl.h" + +size_t +mpc_out_str (FILE *stream, int base, size_t n, mpc_srcptr op, mpc_rnd_t rnd) { + size_t size = 3; /* for '(', ' ' and ')' */ + + if (stream == NULL) + stream = stdout; /* fprintf does not allow NULL as first argument */ + + fprintf (stream, "("); + size += mpfr_out_str (stream, base, n, mpc_realref(op), MPC_RND_RE(rnd)); + fprintf (stream, " "); + size += mpfr_out_str (stream, base, n, mpc_imagref(op), MPC_RND_RE(rnd)); + fprintf (stream, ")"); + + return size; +} diff --git a/mpc/src/pow.c b/mpc/src/pow.c new file mode 100644 index 0000000000..2525644a4c --- /dev/null +++ b/mpc/src/pow.c @@ -0,0 +1,818 @@ +/* mpc_pow -- Raise a complex number to the power of another complex number. + +Copyright (C) 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + +/* Return non-zero iff c+i*d is an exact square (a+i*b)^2, + with a, b both of the form m*2^e with m, e integers. + If so, returns in a+i*b the corresponding square root, with a >= 0. + The variables a, b must not overlap with c, d. + + We have c = a^2 - b^2 and d = 2*a*b. + + If one of a, b is exact, then both are (see algorithms.tex). + + Case 1: a <> 0 and b <> 0. + Let a = m*2^e and b = n*2^f with m, e, n, f integers, m and n odd + (we will treat apart the case a = 0 or b = 0). + Then 2*a*b = m*n*2^(e+f+1), thus necessarily e+f >= -1. + Assume e < 0, then f >= 0, then a^2 - b^2 = m^2*2^(2e) - n^2*2^(2f) cannot + be an integer, since n^2*2^(2f) is an integer, and m^2*2^(2e) is not. + Similarly when f < 0 (and thus e >= 0). + Thus we have e, f >= 0, and a, b are both integers. + Let A = 2a^2, then eliminating b between c = a^2 - b^2 and d = 2*a*b + gives A^2 - 2c*A - d^2 = 0, which has solutions c +/- sqrt(c^2+d^2). + We thus need c^2+d^2 to be a square, and c + sqrt(c^2+d^2) --- the solution + we are interested in --- to be two times a square. Then b = d/(2a) is + necessarily an integer. + + Case 2: a = 0. Then d is necessarily zero, thus it suffices to check + whether c = -b^2, i.e., if -c is a square. + + Case 3: b = 0. Then d is necessarily zero, thus it suffices to check + whether c = a^2, i.e., if c is a square. +*/ +static int +mpc_perfect_square_p (mpz_t a, mpz_t b, mpz_t c, mpz_t d) +{ + if (mpz_cmp_ui (d, 0) == 0) /* case a = 0 or b = 0 */ + { + /* necessarily c < 0 here, since we have already considered the case + where x is real non-negative and y is real */ + MPC_ASSERT (mpz_cmp_ui (c, 0) < 0); + mpz_neg (b, c); + if (mpz_perfect_square_p (b)) /* case 2 above */ + { + mpz_sqrt (b, b); + mpz_set_ui (a, 0); + return 1; /* c + i*d = (0 + i*b)^2 */ + } + } + else /* both a and b are non-zero */ + { + if (mpz_divisible_2exp_p (d, 1) == 0) + return 0; /* d must be even */ + mpz_mul (a, c, c); + mpz_addmul (a, d, d); /* c^2 + d^2 */ + if (mpz_perfect_square_p (a)) + { + mpz_sqrt (a, a); + mpz_add (a, c, a); /* c + sqrt(c^2+d^2) */ + if (mpz_divisible_2exp_p (a, 1)) + { + mpz_tdiv_q_2exp (a, a, 1); + if (mpz_perfect_square_p (a)) + { + mpz_sqrt (a, a); + mpz_tdiv_q_2exp (b, d, 1); /* d/2 */ + mpz_divexact (b, b, a); /* d/(2a) */ + return 1; + } + } + } + } + return 0; /* not a square */ +} + +/* fix the sign of Re(z) or Im(z) in case it is zero, + and Re(x) is zero. + sign_eps is 0 if Re(x) = +0, 1 if Re(x) = -0 + sign_a is the sign bit of Im(x). + Assume y is an integer (does nothing otherwise). +*/ +static void +fix_sign (mpc_ptr z, int sign_eps, int sign_a, mpfr_srcptr y) +{ + int ymod4 = -1; + mpfr_exp_t ey; + mpz_t my; + unsigned long int t; + + mpz_init (my); + + ey = mpfr_get_z_exp (my, y); + /* normalize so that my is odd */ + t = mpz_scan1 (my, 0); + ey += (mpfr_exp_t) t; + mpz_tdiv_q_2exp (my, my, t); + /* y = my*2^ey */ + + /* compute y mod 4 (in case y is an integer) */ + if (ey >= 2) + ymod4 = 0; + else if (ey == 1) + ymod4 = mpz_tstbit (my, 0) * 2; /* correct if my < 0 */ + else if (ey == 0) + { + ymod4 = mpz_tstbit (my, 1) * 2 + mpz_tstbit (my, 0); + if (mpz_cmp_ui (my , 0) < 0) + ymod4 = 4 - ymod4; + } + else /* y is not an integer */ + goto end; + + if (mpfr_zero_p (mpc_realref(z))) + { + /* we assume y is always integer in that case (FIXME: prove it): + (eps+I*a)^y = +0 + I*a^y for y = 1 mod 4 and sign_eps = 0 + (eps+I*a)^y = -0 - I*a^y for y = 3 mod 4 and sign_eps = 0 */ + MPC_ASSERT (ymod4 == 1 || ymod4 == 3); + if ((ymod4 == 3 && sign_eps == 0) || + (ymod4 == 1 && sign_eps == 1)) + mpfr_neg (mpc_realref(z), mpc_realref(z), GMP_RNDZ); + } + else if (mpfr_zero_p (mpc_imagref(z))) + { + /* we assume y is always integer in that case (FIXME: prove it): + (eps+I*a)^y = a^y - 0*I for y = 0 mod 4 and sign_a = sign_eps + (eps+I*a)^y = -a^y +0*I for y = 2 mod 4 and sign_a = sign_eps */ + MPC_ASSERT (ymod4 == 0 || ymod4 == 2); + if ((ymod4 == 0 && sign_a == sign_eps) || + (ymod4 == 2 && sign_a != sign_eps)) + mpfr_neg (mpc_imagref(z), mpc_imagref(z), GMP_RNDZ); + } + + end: + mpz_clear (my); +} + +/* If x^y is exactly representable (with maybe a larger precision than z), + round it in z and return the (mpc) inexact flag in [0, 10]. + + If x^y is not exactly representable, return -1. + + If intermediate computations lead to numbers of more than maxprec bits, + then abort and return -2 (in that case, to avoid loops, mpc_pow_exact + should be called again with a larger value of maxprec). + + Assume one of Re(x) or Im(x) is non-zero, and y is non-zero (y is real). + + Warning: z and x might be the same variable, same for Re(z) or Im(z) and y. + + In case -1 or -2 is returned, z is not modified. +*/ +static int +mpc_pow_exact (mpc_ptr z, mpc_srcptr x, mpfr_srcptr y, mpc_rnd_t rnd, + mpfr_prec_t maxprec) +{ + mpfr_exp_t ec, ed, ey; + mpz_t my, a, b, c, d, u; + unsigned long int t; + int ret = -2; + int sign_rex = mpfr_signbit (mpc_realref(x)); + int sign_imx = mpfr_signbit (mpc_imagref(x)); + int x_imag = mpfr_zero_p (mpc_realref(x)); + int z_is_y = 0; + mpfr_t copy_of_y; + + if (mpc_realref (z) == y || mpc_imagref (z) == y) + { + z_is_y = 1; + mpfr_init2 (copy_of_y, mpfr_get_prec (y)); + mpfr_set (copy_of_y, y, GMP_RNDN); + } + + mpz_init (my); + mpz_init (a); + mpz_init (b); + mpz_init (c); + mpz_init (d); + mpz_init (u); + + ey = mpfr_get_z_exp (my, y); + /* normalize so that my is odd */ + t = mpz_scan1 (my, 0); + ey += (mpfr_exp_t) t; + mpz_tdiv_q_2exp (my, my, t); + /* y = my*2^ey with my odd */ + + if (x_imag) + { + mpz_set_ui (c, 0); + ec = 0; + } + else + ec = mpfr_get_z_exp (c, mpc_realref(x)); + if (mpfr_zero_p (mpc_imagref(x))) + { + mpz_set_ui (d, 0); + ed = ec; + } + else + { + ed = mpfr_get_z_exp (d, mpc_imagref(x)); + if (x_imag) + ec = ed; + } + /* x = c*2^ec + I * d*2^ed */ + /* equalize the exponents of x */ + if (ec < ed) + { + mpz_mul_2exp (d, d, (unsigned long int) (ed - ec)); + if ((mpfr_prec_t) mpz_sizeinbase (d, 2) > maxprec) + goto end; + } + else if (ed < ec) + { + mpz_mul_2exp (c, c, (unsigned long int) (ec - ed)); + if ((mpfr_prec_t) mpz_sizeinbase (c, 2) > maxprec) + goto end; + ec = ed; + } + /* now ec=ed and x = (c + I * d) * 2^ec */ + + /* divide by two if possible */ + if (mpz_cmp_ui (c, 0) == 0) + { + t = mpz_scan1 (d, 0); + mpz_tdiv_q_2exp (d, d, t); + ec += (mpfr_exp_t) t; + } + else if (mpz_cmp_ui (d, 0) == 0) + { + t = mpz_scan1 (c, 0); + mpz_tdiv_q_2exp (c, c, t); + ec += (mpfr_exp_t) t; + } + else /* neither c nor d is zero */ + { + unsigned long v; + t = mpz_scan1 (c, 0); + v = mpz_scan1 (d, 0); + if (v < t) + t = v; + mpz_tdiv_q_2exp (c, c, t); + mpz_tdiv_q_2exp (d, d, t); + ec += (mpfr_exp_t) t; + } + + /* now either one of c, d is odd */ + + while (ey < 0) + { + /* check if x is a square */ + if (ec & 1) + { + mpz_mul_2exp (c, c, 1); + mpz_mul_2exp (d, d, 1); + ec --; + } + /* now ec is even */ + if (mpc_perfect_square_p (a, b, c, d) == 0) + break; + mpz_swap (a, c); + mpz_swap (b, d); + ec /= 2; + ey ++; + } + + if (ey < 0) + { + ret = -1; /* not representable */ + goto end; + } + + /* Now ey >= 0, it thus suffices to check that x^my is representable. + If my > 0, this is always true. If my < 0, we first try to invert + (c+I*d)*2^ec. + */ + if (mpz_cmp_ui (my, 0) < 0) + { + /* If my < 0, 1 / (c + I*d) = (c - I*d)/(c^2 + d^2), thus a sufficient + condition is that c^2 + d^2 is a power of two, assuming |c| <> |d|. + Assume a prime p <> 2 divides c^2 + d^2, + then if p does not divide c or d, 1 / (c + I*d) cannot be exact. + If p divides both c and d, then we can write c = p*c', d = p*d', + and 1 / (c + I*d) = 1/p * 1/(c' + I*d'). This shows that if 1/(c+I*d) + is exact, then 1/(c' + I*d') is exact too, and we are back to the + previous case. In conclusion, a necessary and sufficient condition + is that c^2 + d^2 is a power of two. + */ + /* FIXME: we could first compute c^2+d^2 mod a limb for example */ + mpz_mul (a, c, c); + mpz_addmul (a, d, d); + t = mpz_scan1 (a, 0); + if (mpz_sizeinbase (a, 2) != 1 + t) /* a is not a power of two */ + { + ret = -1; /* not representable */ + goto end; + } + /* replace (c,d) by (c/(c^2+d^2), -d/(c^2+d^2)) */ + mpz_neg (d, d); + ec = -ec - (mpfr_exp_t) t; + mpz_neg (my, my); + } + + /* now ey >= 0 and my >= 0, and we want to compute + [(c + I * d) * 2^ec] ^ (my * 2^ey). + + We first compute [(c + I * d) * 2^ec]^my, then square ey times. */ + t = mpz_sizeinbase (my, 2) - 1; + mpz_set (a, c); + mpz_set (b, d); + ed = ec; + /* invariant: (a + I*b) * 2^ed = ((c + I*d) * 2^ec)^trunc(my/2^t) */ + while (t-- > 0) + { + unsigned long int v, w; + /* square a + I*b */ + mpz_mul (u, a, b); + mpz_mul (a, a, a); + mpz_submul (a, b, b); + mpz_mul_2exp (b, u, 1); + ed *= 2; + if (mpz_tstbit (my, t)) /* multiply by c + I*d */ + { + mpz_mul (u, a, c); + mpz_submul (u, b, d); /* ac-bd */ + mpz_mul (b, b, c); + mpz_addmul (b, a, d); /* bc+ad */ + mpz_swap (a, u); + ed += ec; + } + /* remove powers of two in (a,b) */ + if (mpz_cmp_ui (a, 0) == 0) + { + w = mpz_scan1 (b, 0); + mpz_tdiv_q_2exp (b, b, w); + ed += (mpfr_exp_t) w; + } + else if (mpz_cmp_ui (b, 0) == 0) + { + w = mpz_scan1 (a, 0); + mpz_tdiv_q_2exp (a, a, w); + ed += (mpfr_exp_t) w; + } + else + { + w = mpz_scan1 (a, 0); + v = mpz_scan1 (b, 0); + if (v < w) + w = v; + mpz_tdiv_q_2exp (a, a, w); + mpz_tdiv_q_2exp (b, b, w); + ed += (mpfr_exp_t) w; + } + if ( (mpfr_prec_t) mpz_sizeinbase (a, 2) > maxprec + || (mpfr_prec_t) mpz_sizeinbase (b, 2) > maxprec) + goto end; + } + /* now a+I*b = (c+I*d)^my */ + + while (ey-- > 0) + { + unsigned long sa, sb; + + /* square a + I*b */ + mpz_mul (u, a, b); + mpz_mul (a, a, a); + mpz_submul (a, b, b); + mpz_mul_2exp (b, u, 1); + ed *= 2; + + /* divide by largest 2^n possible, to avoid many loops for e.g., + (2+2*I)^16777216 */ + sa = mpz_scan1 (a, 0); + sb = mpz_scan1 (b, 0); + sa = (sa <= sb) ? sa : sb; + mpz_tdiv_q_2exp (a, a, sa); + mpz_tdiv_q_2exp (b, b, sa); + ed += (mpfr_exp_t) sa; + + if ( (mpfr_prec_t) mpz_sizeinbase (a, 2) > maxprec + || (mpfr_prec_t) mpz_sizeinbase (b, 2) > maxprec) + goto end; + } + + ret = mpfr_set_z (mpc_realref(z), a, MPC_RND_RE(rnd)); + ret = MPC_INEX(ret, mpfr_set_z (mpc_imagref(z), b, MPC_RND_IM(rnd))); + mpfr_mul_2si (mpc_realref(z), mpc_realref(z), ed, MPC_RND_RE(rnd)); + mpfr_mul_2si (mpc_imagref(z), mpc_imagref(z), ed, MPC_RND_IM(rnd)); + + end: + mpz_clear (my); + mpz_clear (a); + mpz_clear (b); + mpz_clear (c); + mpz_clear (d); + mpz_clear (u); + + if (ret >= 0 && x_imag) + fix_sign (z, sign_rex, sign_imx, (z_is_y) ? copy_of_y : y); + + if (z_is_y) + mpfr_clear (copy_of_y); + + return ret; +} + +/* Return 1 if y*2^k is an odd integer, 0 otherwise. + Adapted from MPFR, file pow.c. + + Examples: with k=0, check if y is an odd integer, + with k=1, check if y is half-an-integer, + with k=-1, check if y/2 is an odd integer. +*/ +#define MPFR_LIMB_HIGHBIT ((mp_limb_t) 1 << (BITS_PER_MP_LIMB - 1)) +static int +is_odd (mpfr_srcptr y, mpfr_exp_t k) +{ + mpfr_exp_t expo; + mpfr_prec_t prec; + mp_size_t yn; + mp_limb_t *yp; + + expo = mpfr_get_exp (y) + k; + if (expo <= 0) + return 0; /* |y| < 1 and not 0 */ + + prec = mpfr_get_prec (y); + if ((mpfr_prec_t) expo > prec) + return 0; /* y is a multiple of 2^(expo-prec), thus not odd */ + + /* 0 < expo <= prec: + y = 1xxxxxxxxxt.zzzzzzzzzzzzzzzzzz[000] + expo bits (prec-expo) bits + + We have to check that: + (a) the bit 't' is set + (b) all the 'z' bits are zero + */ + + prec = ((prec - 1) / BITS_PER_MP_LIMB + 1) * BITS_PER_MP_LIMB - expo; + /* number of z+0 bits */ + + yn = prec / BITS_PER_MP_LIMB; + /* yn is the index of limb containing the 't' bit */ + + yp = y->_mpfr_d; + /* if expo is a multiple of BITS_PER_MP_LIMB, t is bit 0 */ + if (expo % BITS_PER_MP_LIMB == 0 ? (yp[yn] & 1) == 0 + : yp[yn] << ((expo % BITS_PER_MP_LIMB) - 1) != MPFR_LIMB_HIGHBIT) + return 0; + while (--yn >= 0) + if (yp[yn] != 0) + return 0; + return 1; +} + +/* Put in z the value of x^y, rounded according to 'rnd'. + Return the inexact flag in [0, 10]. */ +int +mpc_pow (mpc_ptr z, mpc_srcptr x, mpc_srcptr y, mpc_rnd_t rnd) +{ + int ret = -2, loop, x_real, x_imag, y_real, z_real = 0, z_imag = 0; + mpc_t t, u; + mpfr_prec_t p, pr, pi, maxprec; + int saved_underflow, saved_overflow; + + /* save the underflow or overflow flags from MPFR */ + saved_underflow = mpfr_underflow_p (); + saved_overflow = mpfr_overflow_p (); + + x_real = mpfr_zero_p (mpc_imagref(x)); + y_real = mpfr_zero_p (mpc_imagref(y)); + + if (y_real && mpfr_zero_p (mpc_realref(y))) /* case y zero */ + { + if (x_real && mpfr_zero_p (mpc_realref(x))) + { + /* we define 0^0 to be (1, +0) since the real part is + coherent with MPFR where 0^0 gives 1, and the sign of the + imaginary part cannot be determined */ + mpc_set_ui_ui (z, 1, 0, MPC_RNDNN); + return 0; + } + else /* x^0 = 1 +/- i*0 even for x=NaN see algorithms.tex for the + sign of zero */ + { + mpfr_t n; + int inex, cx1; + int sign_zi; + /* cx1 < 0 if |x| < 1 + cx1 = 0 if |x| = 1 + cx1 > 0 if |x| > 1 + */ + mpfr_init (n); + inex = mpc_norm (n, x, GMP_RNDN); + cx1 = mpfr_cmp_ui (n, 1); + if (cx1 == 0 && inex != 0) + cx1 = -inex; + + sign_zi = (cx1 < 0 && mpfr_signbit (mpc_imagref (y)) == 0) + || (cx1 == 0 + && mpfr_signbit (mpc_imagref (x)) != mpfr_signbit (mpc_realref (y))) + || (cx1 > 0 && mpfr_signbit (mpc_imagref (y))); + + /* warning: mpc_set_ui_ui does not set Im(z) to -0 if Im(rnd)=RNDD */ + ret = mpc_set_ui_ui (z, 1, 0, rnd); + + if (MPC_RND_IM (rnd) == GMP_RNDD || sign_zi) + mpc_conj (z, z, MPC_RNDNN); + + mpfr_clear (n); + return ret; + } + } + + if (!mpc_fin_p (x) || !mpc_fin_p (y)) + { + /* special values: exp(y*log(x)) */ + mpc_init2 (u, 2); + mpc_log (u, x, MPC_RNDNN); + mpc_mul (u, u, y, MPC_RNDNN); + ret = mpc_exp (z, u, rnd); + mpc_clear (u); + goto end; + } + + if (x_real) /* case x real */ + { + if (mpfr_zero_p (mpc_realref(x))) /* x is zero */ + { + /* special values: exp(y*log(x)) */ + mpc_init2 (u, 2); + mpc_log (u, x, MPC_RNDNN); + mpc_mul (u, u, y, MPC_RNDNN); + ret = mpc_exp (z, u, rnd); + mpc_clear (u); + goto end; + } + + /* Special case 1^y = 1 */ + if (mpfr_cmp_ui (mpc_realref(x), 1) == 0) + { + int s1, s2; + s1 = mpfr_signbit (mpc_realref (y)); + s2 = mpfr_signbit (mpc_imagref (x)); + + ret = mpc_set_ui (z, +1, rnd); + /* the sign of the zero imaginary part is known in some cases (see + algorithm.tex). In such cases we have + (x +s*0i)^(y+/-0i) = x^y + s*sign(y)*0i + where s = +/-1. We extend here this rule to fix the sign of the + zero part. + + Note that the sign must also be set explicitly when rnd=RNDD + because mpfr_set_ui(z_i, 0, rnd) always sets z_i to +0. + */ + if (MPC_RND_IM (rnd) == GMP_RNDD || s1 != s2) + mpc_conj (z, z, MPC_RNDNN); + goto end; + } + + /* x^y is real when: + (a) x is real and y is integer + (b) x is real non-negative and y is real */ + if (y_real && (mpfr_integer_p (mpc_realref(y)) || + mpfr_cmp_ui (mpc_realref(x), 0) >= 0)) + { + int s1, s2; + s1 = mpfr_signbit (mpc_realref (y)); + s2 = mpfr_signbit (mpc_imagref (x)); + + ret = mpfr_pow (mpc_realref(z), mpc_realref(x), mpc_realref(y), MPC_RND_RE(rnd)); + ret = MPC_INEX(ret, mpfr_set_ui (mpc_imagref(z), 0, MPC_RND_IM(rnd))); + + /* the sign of the zero imaginary part is known in some cases + (see algorithm.tex). In such cases we have (x +s*0i)^(y+/-0i) + = x^y + s*sign(y)*0i where s = +/-1. + We extend here this rule to fix the sign of the zero part. + + Note that the sign must also be set explicitly when rnd=RNDD + because mpfr_set_ui(z_i, 0, rnd) always sets z_i to +0. + */ + if (MPC_RND_IM(rnd) == GMP_RNDD || s1 != s2) + mpfr_neg (mpc_imagref(z), mpc_imagref(z), MPC_RND_IM(rnd)); + goto end; + } + + /* (-1)^(n+I*t) is real for n integer and t real */ + if (mpfr_cmp_si (mpc_realref(x), -1) == 0 && mpfr_integer_p (mpc_realref(y))) + z_real = 1; + + /* for x real, x^y is imaginary when: + (a) x is negative and y is half-an-integer + (b) x = -1 and Re(y) is half-an-integer + */ + if ((mpfr_cmp_ui (mpc_realref(x), 0) < 0) && is_odd (mpc_realref(y), 1) + && (y_real || mpfr_cmp_si (mpc_realref(x), -1) == 0)) + z_imag = 1; + } + else /* x non real */ + /* I^(t*I) and (-I)^(t*I) are real for t real, + I^(n+t*I) and (-I)^(n+t*I) are real for n even and t real, and + I^(n+t*I) and (-I)^(n+t*I) are imaginary for n odd and t real + (s*I)^n is real for n even and imaginary for n odd */ + if ((mpc_cmp_si_si (x, 0, 1) == 0 || mpc_cmp_si_si (x, 0, -1) == 0 || + (mpfr_cmp_ui (mpc_realref(x), 0) == 0 && y_real)) && + mpfr_integer_p (mpc_realref(y))) + { /* x is I or -I, and Re(y) is an integer */ + if (is_odd (mpc_realref(y), 0)) + z_imag = 1; /* Re(y) odd: z is imaginary */ + else + z_real = 1; /* Re(y) even: z is real */ + } + else /* (t+/-t*I)^(2n) is imaginary for n odd and real for n even */ + if (mpfr_cmpabs (mpc_realref(x), mpc_imagref(x)) == 0 && y_real && + mpfr_integer_p (mpc_realref(y)) && is_odd (mpc_realref(y), 0) == 0) + { + if (is_odd (mpc_realref(y), -1)) /* y/2 is odd */ + z_imag = 1; + else + z_real = 1; + } + + pr = mpfr_get_prec (mpc_realref(z)); + pi = mpfr_get_prec (mpc_imagref(z)); + p = (pr > pi) ? pr : pi; + p += 12; /* experimentally, seems to give less than 10% of failures in + Ziv's strategy; probably wrong now since q is not computed */ + if (p < 64) + p = 64; + mpc_init2 (u, p); + mpc_init2 (t, p); + pr += MPC_RND_RE(rnd) == GMP_RNDN; + pi += MPC_RND_IM(rnd) == GMP_RNDN; + maxprec = MPC_MAX_PREC (z); + x_imag = mpfr_zero_p (mpc_realref(x)); + for (loop = 0;; loop++) + { + int ret_exp; + mpfr_exp_t dr, di; + mpfr_prec_t q; + + mpc_log (t, x, MPC_RNDNN); + mpc_mul (t, t, y, MPC_RNDNN); + + /* Compute q such that |Re (y log x)|, |Im (y log x)| < 2^q. + We recompute it at each loop since we might get different + bounds if the precision is not enough. */ + q = mpfr_get_exp (mpc_realref(t)) > 0 ? mpfr_get_exp (mpc_realref(t)) : 0; + if (mpfr_get_exp (mpc_imagref(t)) > (mpfr_exp_t) q) + q = mpfr_get_exp (mpc_imagref(t)); + + mpfr_clear_overflow (); + mpfr_clear_underflow (); + ret_exp = mpc_exp (u, t, MPC_RNDNN); + if (mpfr_underflow_p () || mpfr_overflow_p ()) { + /* under- and overflow flags are set by mpc_exp */ + mpc_set (z, u, MPC_RNDNN); + ret = ret_exp; + goto exact; + } + + /* Since the error bound is global, we have to take into account the + exponent difference between the real and imaginary parts. We assume + either the real or the imaginary part of u is not zero. + */ + dr = mpfr_zero_p (mpc_realref(u)) ? mpfr_get_exp (mpc_imagref(u)) + : mpfr_get_exp (mpc_realref(u)); + di = mpfr_zero_p (mpc_imagref(u)) ? dr : mpfr_get_exp (mpc_imagref(u)); + if (dr > di) + { + di = dr - di; + dr = 0; + } + else + { + dr = di - dr; + di = 0; + } + /* the term -3 takes into account the factor 4 in the complex error + (see algorithms.tex) plus one due to the exponent difference: if + z = a + I*b, where the relative error on z is at most 2^(-p), and + EXP(a) = EXP(b) + k, the relative error on b is at most 2^(k-p) */ + if ((z_imag || (p > q + 3 + dr && mpfr_can_round (mpc_realref(u), p - q - 3 - dr, GMP_RNDN, GMP_RNDZ, pr))) && + (z_real || (p > q + 3 + di && mpfr_can_round (mpc_imagref(u), p - q - 3 - di, GMP_RNDN, GMP_RNDZ, pi)))) + break; + + /* if Re(u) is not known to be zero, assume it is a normal number, i.e., + neither zero, Inf or NaN, otherwise we might enter an infinite loop */ + MPC_ASSERT (z_imag || mpfr_number_p (mpc_realref(u))); + /* idem for Im(u) */ + MPC_ASSERT (z_real || mpfr_number_p (mpc_imagref(u))); + + if (ret == -2) /* we did not yet call mpc_pow_exact, or it aborted + because intermediate computations had > maxprec bits */ + { + /* check exact cases (see algorithms.tex) */ + if (y_real) + { + maxprec *= 2; + ret = mpc_pow_exact (z, x, mpc_realref(y), rnd, maxprec); + if (ret != -1 && ret != -2) + goto exact; + } + p += dr + di + 64; + } + else + p += p / 2; + mpc_set_prec (t, p); + mpc_set_prec (u, p); + } + + if (z_real) + { + /* When the result is real (see algorithm.tex for details), + Im(x^y) = + + sign(imag(y))*0i, if |x| > 1 + + sign(imag(x))*sign(real(y))*0i, if |x| = 1 + - sign(imag(y))*0i, if |x| < 1 + */ + mpfr_t n; + int inex, cx1; + int sign_zi, sign_rex, sign_imx; + /* cx1 < 0 if |x| < 1 + cx1 = 0 if |x| = 1 + cx1 > 0 if |x| > 1 + */ + + sign_rex = mpfr_signbit (mpc_realref (x)); + sign_imx = mpfr_signbit (mpc_imagref (x)); + mpfr_init (n); + inex = mpc_norm (n, x, GMP_RNDN); + cx1 = mpfr_cmp_ui (n, 1); + if (cx1 == 0 && inex != 0) + cx1 = -inex; + + sign_zi = (cx1 < 0 && mpfr_signbit (mpc_imagref (y)) == 0) + || (cx1 == 0 && sign_imx != mpfr_signbit (mpc_realref (y))) + || (cx1 > 0 && mpfr_signbit (mpc_imagref (y))); + + /* copy RE(y) to n since if z==y we will destroy Re(y) below */ + mpfr_set_prec (n, mpfr_get_prec (mpc_realref (y))); + mpfr_set (n, mpc_realref (y), GMP_RNDN); + ret = mpfr_set (mpc_realref(z), mpc_realref(u), MPC_RND_RE(rnd)); + if (y_real && (x_real || x_imag)) + { + /* FIXME: with y_real we assume Im(y) is really 0, which is the case + for example when y comes from pow_fr, but in case Im(y) is +0 or + -0, we might get different results */ + mpfr_set_ui (mpc_imagref (z), 0, MPC_RND_IM (rnd)); + fix_sign (z, sign_rex, sign_imx, n); + ret = MPC_INEX(ret, 0); /* imaginary part is exact */ + } + else + { + ret = MPC_INEX (ret, mpfr_set_ui (mpc_imagref (z), 0, MPC_RND_IM (rnd))); + /* warning: mpfr_set_ui does not set Im(z) to -0 if Im(rnd) = RNDD */ + if (MPC_RND_IM (rnd) == GMP_RNDD || sign_zi) + mpc_conj (z, z, MPC_RNDNN); + } + + mpfr_clear (n); + } + else if (z_imag) + { + ret = mpfr_set (mpc_imagref(z), mpc_imagref(u), MPC_RND_IM(rnd)); + /* if z is imaginary and y real, then x cannot be real */ + if (y_real && x_imag) + { + int sign_rex = mpfr_signbit (mpc_realref (x)); + + /* If z overlaps with y we set Re(z) before checking Re(y) below, + but in that case y=0, which was dealt with above. */ + mpfr_set_ui (mpc_realref (z), 0, MPC_RND_RE (rnd)); + /* Note: fix_sign only does something when y is an integer, + then necessarily y = 1 or 3 (mod 4), and in that case the + sign of Im(x) is irrelevant. */ + fix_sign (z, sign_rex, 0, mpc_realref (y)); + ret = MPC_INEX(0, ret); + } + else + ret = MPC_INEX(mpfr_set_ui (mpc_realref(z), 0, MPC_RND_RE(rnd)), ret); + } + else + ret = mpc_set (z, u, rnd); + exact: + mpc_clear (t); + mpc_clear (u); + + /* restore underflow and overflow flags from MPFR */ + if (saved_underflow) + mpfr_set_underflow (); + if (saved_overflow) + mpfr_set_overflow (); + + end: + return ret; +} diff --git a/mpc/src/pow_d.c b/mpc/src/pow_d.c new file mode 100644 index 0000000000..8034b26293 --- /dev/null +++ b/mpc/src/pow_d.c @@ -0,0 +1,38 @@ +/* mpc_pow_d -- Raise a complex number to a double-precision power. + +Copyright (C) 2009 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include <float.h> /* for DBL_MANT_DIG */ +#include "mpc-impl.h" + +int +mpc_pow_d (mpc_ptr z, mpc_srcptr x, double y, mpc_rnd_t rnd) +{ + mpc_t yy; + int inex; + + MPC_ASSERT(FLT_RADIX == 2); + mpc_init3 (yy, DBL_MANT_DIG, MPFR_PREC_MIN); + mpc_set_d (yy, y, MPC_RNDNN); /* exact */ + inex = mpc_pow (z, x, yy, rnd); + mpc_clear (yy); + return inex; +} + diff --git a/mpc/src/pow_fr.c b/mpc/src/pow_fr.c new file mode 100644 index 0000000000..8c5d930421 --- /dev/null +++ b/mpc/src/pow_fr.c @@ -0,0 +1,37 @@ +/* mpc_pow_fr -- Raise a complex number to a floating-point power. + +Copyright (C) 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_pow_fr (mpc_ptr z, mpc_srcptr x, mpfr_srcptr y, mpc_rnd_t rnd) +{ + mpc_t yy; + int inex; + + /* avoid copying the significand of y by copying only the struct */ + mpc_realref(yy)[0] = y[0]; + mpfr_init2 (mpc_imagref(yy), MPFR_PREC_MIN); + mpfr_set_ui (mpc_imagref(yy), 0, GMP_RNDN); + inex = mpc_pow (z, x, yy, rnd); + mpfr_clear (mpc_imagref(yy)); + return inex; +} + diff --git a/mpc/src/pow_ld.c b/mpc/src/pow_ld.c new file mode 100644 index 0000000000..9d98a98478 --- /dev/null +++ b/mpc/src/pow_ld.c @@ -0,0 +1,38 @@ +/* mpc_pow_ld -- Raise a complex number to a long double power. + +Copyright (C) 2009 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include <float.h> /* for LDBL_MANT_DIG */ +#include "mpc-impl.h" + +int +mpc_pow_ld (mpc_ptr z, mpc_srcptr x, long double y, mpc_rnd_t rnd) +{ + mpc_t yy; + int inex; + + MPC_ASSERT(FLT_RADIX == 2); + mpc_init3 (yy, LDBL_MANT_DIG, MPFR_PREC_MIN); + mpc_set_ld (yy, y, MPC_RNDNN); /* exact */ + inex = mpc_pow (z, x, yy, rnd); + mpc_clear (yy); + return inex; +} + diff --git a/mpc/src/pow_si.c b/mpc/src/pow_si.c new file mode 100644 index 0000000000..5b5c5d9dfb --- /dev/null +++ b/mpc/src/pow_si.c @@ -0,0 +1,30 @@ +/* mpc_pow_si -- Raise a complex number to an integer power. + +Copyright (C) 2009, 2010 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_pow_si (mpc_ptr z, mpc_srcptr x, long y, mpc_rnd_t rnd) +{ + if (y >= 0) + return mpc_pow_usi (z, x, (unsigned long) y, 1, rnd); + else + return mpc_pow_usi (z, x, (unsigned long) (-y), -1, rnd); +} diff --git a/mpc/src/pow_ui.c b/mpc/src/pow_ui.c new file mode 100644 index 0000000000..da82a9471f --- /dev/null +++ b/mpc/src/pow_ui.c @@ -0,0 +1,169 @@ +/* mpc_pow_ui -- Raise a complex number to an integer power. + +Copyright (C) 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <limits.h> /* for CHAR_BIT */ +#include "mpc-impl.h" + +static int +mpc_pow_usi_naive (mpc_ptr z, mpc_srcptr x, unsigned long y, int sign, + mpc_rnd_t rnd) +{ + int inex; + mpc_t t; + + mpc_init3 (t, sizeof (unsigned long) * CHAR_BIT, MPFR_PREC_MIN); + if (sign > 0) + mpc_set_ui (t, y, MPC_RNDNN); /* exact */ + else + mpc_set_si (t, - (signed long) y, MPC_RNDNN); + inex = mpc_pow (z, x, t, rnd); + mpc_clear (t); + + return inex; +} + + +int +mpc_pow_usi (mpc_ptr z, mpc_srcptr x, unsigned long y, int sign, + mpc_rnd_t rnd) + /* computes z = x^(sign*y) */ +{ + int inex; + mpc_t t, x3; + mpfr_prec_t p, l, l0; + long unsigned int u; + int has3; /* non-zero if y has '11' in its binary representation */ + int loop, done; + + /* let mpc_pow deal with special values */ + if (!mpc_fin_p (x) || mpfr_zero_p (mpc_realref (x)) || mpfr_zero_p (mpc_imagref(x)) + || y == 0) + return mpc_pow_usi_naive (z, x, y, sign, rnd); + /* easy special cases */ + else if (y == 1) { + if (sign > 0) + return mpc_set (z, x, rnd); + else + return mpc_ui_div (z, 1ul, x, rnd); + } + else if (y == 2 && sign > 0) + return mpc_sqr (z, x, rnd); + /* let mpc_pow treat potential over- and underflows */ + else { + mpfr_exp_t exp_r = mpfr_get_exp (mpc_realref (x)), + exp_i = mpfr_get_exp (mpc_imagref (x)); + if ( MPC_MAX (exp_r, exp_i) > mpfr_get_emax () / (mpfr_exp_t) y + /* heuristic for overflow */ + || MPC_MAX (-exp_r, -exp_i) > (-mpfr_get_emin ()) / (mpfr_exp_t) y + /* heuristic for underflow */ + ) + return mpc_pow_usi_naive (z, x, y, sign, rnd); + } + + has3 = (y & (y >> 1)) != 0; + for (l = 0, u = y; u > 3; l ++, u >>= 1); + /* l>0 is the number of bits of y, minus 2, thus y has bits: + y_{l+1} y_l y_{l-1} ... y_1 y_0 */ + l0 = l + 2; + p = MPC_MAX_PREC(z) + l0 + 32; /* l0 ensures that y*2^{-p} <= 1 below */ + mpc_init2 (t, p); + if (has3) + mpc_init2 (x3, p); + + loop = 0; + done = 0; + while (!done) { + loop++; + + mpc_sqr (t, x, MPC_RNDNN); + if (has3) { + mpc_mul (x3, t, x, MPC_RNDNN); + if ((y >> l) & 1) /* y starts with 11... */ + mpc_set (t, x3, MPC_RNDNN); + } + while (l-- > 0) { + mpc_sqr (t, t, MPC_RNDNN); + if ((y >> l) & 1) { + if ((l > 0) && ((y >> (l-1)) & 1)) /* implies has3 <> 0 */ { + l--; + mpc_sqr (t, t, MPC_RNDNN); + mpc_mul (t, t, x3, MPC_RNDNN); + } + else + mpc_mul (t, t, x, MPC_RNDNN); + } + } + if (sign < 0) + mpc_ui_div (t, 1ul, t, MPC_RNDNN); + + if (mpfr_zero_p (mpc_realref(t)) || mpfr_zero_p (mpc_imagref(t))) { + inex = mpc_pow_usi_naive (z, x, y, sign, rnd); + /* since mpfr_get_exp() is not defined for zero */ + done = 1; + } + else { + /* see error bound in algorithms.tex; we use y<2^l0 instead of y-1 + also when sign>0 */ + mpfr_exp_t diff; + mpfr_prec_t er, ei; + + diff = mpfr_get_exp (mpc_realref(t)) - mpfr_get_exp (mpc_imagref(t)); + /* the factor on the real part is 2+2^(-diff+2) <= 4 for diff >= 1 + and < 2^(-diff+3) for diff <= 0 */ + er = (diff >= 1) ? l0 + 3 : l0 + (-diff) + 3; + /* the factor on the imaginary part is 2+2^(diff+2) <= 4 for diff <= -1 + and < 2^(diff+3) for diff >= 0 */ + ei = (diff <= -1) ? l0 + 3 : l0 + diff + 3; + if (mpfr_can_round (mpc_realref(t), p - er, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE(z) + (MPC_RND_RE(rnd) == GMP_RNDN)) + && mpfr_can_round (mpc_imagref(t), p - ei, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM(z) + (MPC_RND_IM(rnd) == GMP_RNDN))) { + inex = mpc_set (z, t, rnd); + done = 1; + } + else if (loop == 1 && SAFE_ABS(mpfr_prec_t, diff) < MPC_MAX_PREC(z)) { + /* common case, make a second trial at higher precision */ + p += MPC_MAX_PREC(x); + mpc_set_prec (t, p); + if (has3) + mpc_set_prec (x3, p); + l = l0 - 2; + } + else { + /* stop the loop and use mpc_pow */ + inex = mpc_pow_usi_naive (z, x, y, sign, rnd); + done = 1; + } + } + } + + mpc_clear (t); + if (has3) + mpc_clear (x3); + + return inex; +} + + +int +mpc_pow_ui (mpc_ptr z, mpc_srcptr x, unsigned long y, mpc_rnd_t rnd) +{ + return mpc_pow_usi (z, x, y, 1, rnd); +} diff --git a/mpc/src/pow_z.c b/mpc/src/pow_z.c new file mode 100644 index 0000000000..22eb544ca9 --- /dev/null +++ b/mpc/src/pow_z.c @@ -0,0 +1,47 @@ +/* mpc_pow_z -- Raise a complex number to an integer power. + +Copyright (C) 2009, 2010 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_pow_z (mpc_ptr z, mpc_srcptr x, mpz_srcptr y, mpc_rnd_t rnd) +{ + mpc_t yy; + int inex; + mpfr_prec_t n = (mpfr_prec_t) mpz_sizeinbase (y, 2); + + /* if y fits in an unsigned long or long, call the corresponding functions, + which are supposed to be more efficient */ + if (mpz_cmp_ui (y, 0ul) >= 0) { + if (mpz_fits_ulong_p (y)) + return mpc_pow_usi (z, x, mpz_get_ui (y), 1, rnd); + } + else { + if (mpz_fits_slong_p (y)) + return mpc_pow_usi (z, x, (unsigned long) (-mpz_get_si (y)), -1, rnd); + } + + mpc_init3 (yy, (n < MPFR_PREC_MIN) ? MPFR_PREC_MIN : n, MPFR_PREC_MIN); + mpc_set_z (yy, y, MPC_RNDNN); /* exact */ + inex = mpc_pow (z, x, yy, rnd); + mpc_clear (yy); + return inex; +} + diff --git a/mpc/src/proj.c b/mpc/src/proj.c new file mode 100644 index 0000000000..ace58c5331 --- /dev/null +++ b/mpc/src/proj.c @@ -0,0 +1,34 @@ +/* mpc_proj -- projection of a complex number onto the Riemann sphere. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_proj (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd) +{ + if (mpc_inf_p (b)) { + /* infinities project to +Inf +i* copysign(0.0, cimag(z)) */ + mpfr_set_inf (mpc_realref (a), +1); + mpfr_set_zero (mpc_imagref (a), (mpfr_signbit (mpc_imagref (b)) ? -1 : 1)); + return MPC_INEX (0, 0); + } + else + return mpc_set (a, b, rnd); +} diff --git a/mpc/src/real.c b/mpc/src/real.c new file mode 100644 index 0000000000..041dddb5ff --- /dev/null +++ b/mpc/src/real.c @@ -0,0 +1,27 @@ +/* mpc_real -- Get the real part of a complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_real (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd) +{ + return mpfr_set (a, mpc_realref (b), rnd); +} diff --git a/mpc/src/set.c b/mpc/src/set.c new file mode 100644 index 0000000000..e7a3c1148c --- /dev/null +++ b/mpc/src/set.c @@ -0,0 +1,32 @@ +/* mpc_set -- Set a complex number from another complex number. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_set (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_set (mpc_realref(a), mpc_realref(b), MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/set_prec.c b/mpc/src/set_prec.c new file mode 100644 index 0000000000..c5e6f24a37 --- /dev/null +++ b/mpc/src/set_prec.c @@ -0,0 +1,28 @@ +/* mpc_set_prec -- reset the precision of a complex variable. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_set_prec (mpc_t x, mpfr_prec_t prec) +{ + mpfr_set_prec (mpc_realref(x), prec); + mpfr_set_prec (mpc_imagref(x), prec); +} diff --git a/mpc/src/set_str.c b/mpc/src/set_str.c new file mode 100644 index 0000000000..195b9ac0bf --- /dev/null +++ b/mpc/src/set_str.c @@ -0,0 +1,42 @@ +/* mpc_set_str -- Convert a string into a complex number. + +Copyright (C) 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <ctype.h> +#include "mpc-impl.h" + +int +mpc_set_str (mpc_t z, const char *str, int base, mpc_rnd_t rnd) +{ + char *p; + int inex; + + inex = mpc_strtoc (z, str, &p, base, rnd); + + if (inex != -1){ + while (isspace ((unsigned char) (*p))) + p++; + if (*p == '\0') + return inex; + } + + mpfr_set_nan (mpc_realref (z)); + mpfr_set_nan (mpc_imagref (z)); + return -1; +} diff --git a/mpc/src/set_x.c b/mpc/src/set_x.c new file mode 100644 index 0000000000..94ec12d96b --- /dev/null +++ b/mpc/src/set_x.c @@ -0,0 +1,104 @@ +/* mpc_set_x -- Set the real part of a complex number + (imaginary part equals +0 regardless of rounding mode). + +Copyright (C) 2008, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "config.h" + +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> /* for intmax_t */ +#else +# ifdef HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#ifdef HAVE_COMPLEX_H +# include <complex.h> +#endif + +#include "mpc-impl.h" + +#define MPC_SET_X(real_t, z, real_value, rnd) \ + { \ + int _inex_re, _inex_im; \ + _inex_re = (mpfr_set_ ## real_t) (mpc_realref (z), (real_value), MPC_RND_RE (rnd)); \ + _inex_im = mpfr_set_ui (mpc_imagref (z), 0, MPC_RND_IM (rnd)); \ + return MPC_INEX (_inex_re, _inex_im); \ + } + +int +mpc_set_fr (mpc_ptr a, mpfr_srcptr b, mpc_rnd_t rnd) + MPC_SET_X (fr, a, b, rnd) + +int +mpc_set_d (mpc_ptr a, double b, mpc_rnd_t rnd) + MPC_SET_X (d, a, b, rnd) + +int +mpc_set_ld (mpc_ptr a, long double b, mpc_rnd_t rnd) + MPC_SET_X (ld, a, b, rnd) + +int +mpc_set_ui (mpc_ptr a, unsigned long int b, mpc_rnd_t rnd) + MPC_SET_X (ui, a, b, rnd) + +int +mpc_set_si (mpc_ptr a, long int b, mpc_rnd_t rnd) + MPC_SET_X (si, a, b, rnd) + +int +mpc_set_z (mpc_ptr a, mpz_srcptr b, mpc_rnd_t rnd) + MPC_SET_X (z, a, b, rnd) + +int +mpc_set_q (mpc_ptr a, mpq_srcptr b, mpc_rnd_t rnd) + MPC_SET_X (q, a, b, rnd) + +int +mpc_set_f (mpc_ptr a, mpf_srcptr b, mpc_rnd_t rnd) + MPC_SET_X (f, a, b, rnd) + +#ifdef _MPC_H_HAVE_INTMAX_T +int +mpc_set_uj (mpc_ptr a, uintmax_t b, mpc_rnd_t rnd) + MPC_SET_X (uj, a, b, rnd) + +int +mpc_set_sj (mpc_ptr a, intmax_t b, mpc_rnd_t rnd) + MPC_SET_X (sj, a, b, rnd) +#endif + +#ifdef HAVE_COMPLEX_H +int +mpc_set_dc (mpc_ptr a, double _Complex b, mpc_rnd_t rnd) { + return mpc_set_d_d (a, creal (b), cimag (b), rnd); +} + +int +mpc_set_ldc (mpc_ptr a, long double _Complex b, mpc_rnd_t rnd) { + return mpc_set_ld_ld (a, creall (b), cimagl (b), rnd); +} +#endif + +void +mpc_set_nan (mpc_ptr a) { + mpfr_set_nan (mpc_realref (a)); + mpfr_set_nan (mpc_imagref (a)); +} diff --git a/mpc/src/set_x_x.c b/mpc/src/set_x_x.c new file mode 100644 index 0000000000..3ce69f2454 --- /dev/null +++ b/mpc/src/set_x_x.c @@ -0,0 +1,78 @@ +/* mpc_set_x_x -- Set complex number real and imaginary parts from parameters + whose type is known by mpfr. + +Copyright (C) 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "config.h" + +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> /* for intmax_t */ +#else +# ifdef HAVE_STDINT_H +# include <stdint.h> +# endif +#endif + +#include "mpc-impl.h" + +#define MPC_SET_X_X(type, z, real_value, imag_value, rnd) \ + MPC_SET_X_Y (type, type, z, real_value, imag_value, rnd) + +int +mpc_set_d_d (mpc_ptr z, double a, double b, mpc_rnd_t rnd) + MPC_SET_X_X (d, z, a, b, rnd) + +int +mpc_set_f_f (mpc_ptr z, mpf_srcptr a, mpf_srcptr b, mpc_rnd_t rnd) + MPC_SET_X_X (f, z, a, b, rnd) + +int +mpc_set_fr_fr (mpc_ptr z, mpfr_srcptr a, mpfr_srcptr b, mpc_rnd_t rnd) + MPC_SET_X_X (fr, z, a, b, rnd) + +int +mpc_set_ld_ld (mpc_ptr z, long double a, long double b, mpc_rnd_t rnd) + MPC_SET_X_X (ld, z, a, b, rnd) + +int +mpc_set_q_q (mpc_ptr z, mpq_srcptr a, mpq_srcptr b, mpc_rnd_t rnd) + MPC_SET_X_X (q, z, a, b, rnd) + +int +mpc_set_si_si (mpc_ptr z, long int a, long int b, mpc_rnd_t rnd) + MPC_SET_X_X (si, z, a, b, rnd) + +int +mpc_set_ui_ui (mpc_ptr z, unsigned long int a, unsigned long int b, + mpc_rnd_t rnd) + MPC_SET_X_X (ui, z, a, b, rnd) + +int +mpc_set_z_z (mpc_ptr z, mpz_srcptr a, mpz_srcptr b, mpc_rnd_t rnd) + MPC_SET_X_X (z, z, a, b, rnd) + +#ifdef _MPC_H_HAVE_INTMAX_T +int +mpc_set_uj_uj (mpc_ptr z, uintmax_t a, uintmax_t b, mpc_rnd_t rnd) + MPC_SET_X_X (uj, z, a, b, rnd) + +int +mpc_set_sj_sj (mpc_ptr z, intmax_t a, intmax_t b, mpc_rnd_t rnd) + MPC_SET_X_X (sj, z, a, b, rnd) +#endif diff --git a/mpc/src/sin.c b/mpc/src/sin.c new file mode 100644 index 0000000000..27df7618df --- /dev/null +++ b/mpc/src/sin.c @@ -0,0 +1,27 @@ +/* mpc_sin -- sine of a complex number. + +Copyright (C) 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_sin (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + return MPC_INEX1 (mpc_sin_cos (rop, NULL, op, rnd, 0)); +} diff --git a/mpc/src/sin_cos.c b/mpc/src/sin_cos.c new file mode 100644 index 0000000000..0cff45acd5 --- /dev/null +++ b/mpc/src/sin_cos.c @@ -0,0 +1,402 @@ +/* mpc_sin_cos -- combined sine and cosine of a complex number. + +Copyright (C) 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +static int +mpc_sin_cos_nonfinite (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, + mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) + /* assumes that op (that is, its real or imaginary part) is not finite */ +{ + int overlap; + mpc_t op_loc; + + overlap = (rop_sin == op || rop_cos == op); + if (overlap) { + mpc_init3 (op_loc, MPC_PREC_RE (op), MPC_PREC_IM (op)); + mpc_set (op_loc, op, MPC_RNDNN); + } + else + op_loc [0] = op [0]; + + if (rop_sin != NULL) { + if (mpfr_nan_p (mpc_realref (op_loc)) || mpfr_nan_p (mpc_imagref (op_loc))) { + mpc_set (rop_sin, op_loc, rnd_sin); + if (mpfr_nan_p (mpc_imagref (op_loc))) { + /* sin(x +i*NaN) = NaN +i*NaN, except for x=0 */ + /* sin(-0 +i*NaN) = -0 +i*NaN */ + /* sin(+0 +i*NaN) = +0 +i*NaN */ + if (!mpfr_zero_p (mpc_realref (op_loc))) + mpfr_set_nan (mpc_realref (rop_sin)); + } + else /* op = NaN + i*y */ + if (!mpfr_inf_p (mpc_imagref (op_loc)) && !mpfr_zero_p (mpc_imagref (op_loc))) + /* sin(NaN -i*Inf) = NaN -i*Inf */ + /* sin(NaN -i*0) = NaN -i*0 */ + /* sin(NaN +i*0) = NaN +i*0 */ + /* sin(NaN +i*Inf) = NaN +i*Inf */ + /* sin(NaN +i*y) = NaN +i*NaN, when 0<|y|<Inf */ + mpfr_set_nan (mpc_imagref (rop_sin)); + } + else if (mpfr_inf_p (mpc_realref (op_loc))) { + mpfr_set_nan (mpc_realref (rop_sin)); + + if (!mpfr_inf_p (mpc_imagref (op_loc)) && !mpfr_zero_p (mpc_imagref (op_loc))) + /* sin(+/-Inf +i*y) = NaN +i*NaN, when 0<|y|<Inf */ + mpfr_set_nan (mpc_imagref (rop_sin)); + else + /* sin(+/-Inf -i*Inf) = NaN -i*Inf */ + /* sin(+/-Inf +i*Inf) = NaN +i*Inf */ + /* sin(+/-Inf -i*0) = NaN -i*0 */ + /* sin(+/-Inf +i*0) = NaN +i*0 */ + mpfr_set (mpc_imagref (rop_sin), mpc_imagref (op_loc), MPC_RND_IM (rnd_sin)); + } + else if (mpfr_zero_p (mpc_realref (op_loc))) { + /* sin(-0 -i*Inf) = -0 -i*Inf */ + /* sin(+0 -i*Inf) = +0 -i*Inf */ + /* sin(-0 +i*Inf) = -0 +i*Inf */ + /* sin(+0 +i*Inf) = +0 +i*Inf */ + mpc_set (rop_sin, op_loc, rnd_sin); + } + else { + /* sin(x -i*Inf) = +Inf*(sin(x) -i*cos(x)) */ + /* sin(x +i*Inf) = +Inf*(sin(x) +i*cos(x)) */ + mpfr_t s, c; + mpfr_init2 (s, 2); + mpfr_init2 (c, 2); + mpfr_sin_cos (s, c, mpc_realref (op_loc), GMP_RNDZ); + mpfr_set_inf (mpc_realref (rop_sin), MPFR_SIGN (s)); + mpfr_set_inf (mpc_imagref (rop_sin), MPFR_SIGN (c)*MPFR_SIGN (mpc_imagref (op_loc))); + mpfr_clear (s); + mpfr_clear (c); + } + } + + if (rop_cos != NULL) { + if (mpfr_nan_p (mpc_realref (op_loc))) { + /* cos(NaN + i * NaN) = NaN + i * NaN */ + /* cos(NaN - i * Inf) = +Inf + i * NaN */ + /* cos(NaN + i * Inf) = +Inf + i * NaN */ + /* cos(NaN - i * 0) = NaN - i * 0 */ + /* cos(NaN + i * 0) = NaN + i * 0 */ + /* cos(NaN + i * y) = NaN + i * NaN, when y != 0 */ + if (mpfr_inf_p (mpc_imagref (op_loc))) + mpfr_set_inf (mpc_realref (rop_cos), +1); + else + mpfr_set_nan (mpc_realref (rop_cos)); + + if (mpfr_zero_p (mpc_imagref (op_loc))) + mpfr_set (mpc_imagref (rop_cos), mpc_imagref (op_loc), MPC_RND_IM (rnd_cos)); + else + mpfr_set_nan (mpc_imagref (rop_cos)); + } + else if (mpfr_nan_p (mpc_imagref (op_loc))) { + /* cos(-Inf + i * NaN) = NaN + i * NaN */ + /* cos(+Inf + i * NaN) = NaN + i * NaN */ + /* cos(-0 + i * NaN) = NaN - i * 0 */ + /* cos(+0 + i * NaN) = NaN + i * 0 */ + /* cos(x + i * NaN) = NaN + i * NaN, when x != 0 */ + if (mpfr_zero_p (mpc_realref (op_loc))) + mpfr_set (mpc_imagref (rop_cos), mpc_realref (op_loc), MPC_RND_IM (rnd_cos)); + else + mpfr_set_nan (mpc_imagref (rop_cos)); + + mpfr_set_nan (mpc_realref (rop_cos)); + } + else if (mpfr_inf_p (mpc_realref (op_loc))) { + /* cos(-Inf -i*Inf) = cos(+Inf +i*Inf) = -Inf +i*NaN */ + /* cos(-Inf +i*Inf) = cos(+Inf -i*Inf) = +Inf +i*NaN */ + /* cos(-Inf -i*0) = cos(+Inf +i*0) = NaN -i*0 */ + /* cos(-Inf +i*0) = cos(+Inf -i*0) = NaN +i*0 */ + /* cos(-Inf +i*y) = cos(+Inf +i*y) = NaN +i*NaN, when y != 0 */ + + const int same_sign = + mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc)); + + if (mpfr_inf_p (mpc_imagref (op_loc))) + mpfr_set_inf (mpc_realref (rop_cos), (same_sign ? -1 : +1)); + else + mpfr_set_nan (mpc_realref (rop_cos)); + + if (mpfr_zero_p (mpc_imagref (op_loc))) + mpfr_setsign (mpc_imagref (rop_cos), mpc_imagref (op_loc), same_sign, + MPC_RND_IM(rnd_cos)); + else + mpfr_set_nan (mpc_imagref (rop_cos)); + } + else if (mpfr_zero_p (mpc_realref (op_loc))) { + /* cos(-0 -i*Inf) = cos(+0 +i*Inf) = +Inf -i*0 */ + /* cos(-0 +i*Inf) = cos(+0 -i*Inf) = +Inf +i*0 */ + const int same_sign = + mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc)); + + mpfr_setsign (mpc_imagref (rop_cos), mpc_realref (op_loc), same_sign, + MPC_RND_IM (rnd_cos)); + mpfr_set_inf (mpc_realref (rop_cos), +1); + } + else { + /* cos(x -i*Inf) = +Inf*cos(x) +i*Inf*sin(x), when x != 0 */ + /* cos(x +i*Inf) = +Inf*cos(x) -i*Inf*sin(x), when x != 0 */ + mpfr_t s, c; + mpfr_init2 (c, 2); + mpfr_init2 (s, 2); + mpfr_sin_cos (s, c, mpc_realref (op_loc), GMP_RNDN); + mpfr_set_inf (mpc_realref (rop_cos), mpfr_sgn (c)); + mpfr_set_inf (mpc_imagref (rop_cos), + (mpfr_sgn (mpc_imagref (op_loc)) == mpfr_sgn (s) ? -1 : +1)); + mpfr_clear (s); + mpfr_clear (c); + } + } + + if (overlap) + mpc_clear (op_loc); + + return MPC_INEX12 (MPC_INEX (0,0), MPC_INEX (0,0)); + /* everything is exact */ +} + + +static int +mpc_sin_cos_real (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, + mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) + /* assumes that op is real */ +{ + int inex_sin_re = 0, inex_cos_re = 0; + /* Until further notice, assume computations exact; in particular, + by definition, for not computed values. */ + mpfr_t s, c; + int inex_s, inex_c; + int sign_im = mpfr_signbit (mpc_imagref (op)); + + /* sin(x +-0*i) = sin(x) +-0*i*sign(cos(x)) */ + /* cos(x +-i*0) = cos(x) -+i*0*sign(sin(x)) */ + if (rop_sin != 0) + mpfr_init2 (s, MPC_PREC_RE (rop_sin)); + else + mpfr_init2 (s, 2); /* We need only the sign. */ + if (rop_cos != NULL) + mpfr_init2 (c, MPC_PREC_RE (rop_cos)); + else + mpfr_init2 (c, 2); + inex_s = mpfr_sin (s, mpc_realref (op), MPC_RND_RE (rnd_sin)); + inex_c = mpfr_cos (c, mpc_realref (op), MPC_RND_RE (rnd_cos)); + /* We cannot use mpfr_sin_cos since we may need two distinct rounding + modes and the exact return values. If we need only the sign, an + arbitrary rounding mode will work. */ + + if (rop_sin != NULL) { + mpfr_set (mpc_realref (rop_sin), s, GMP_RNDN); /* exact */ + inex_sin_re = inex_s; + mpfr_set_zero (mpc_imagref (rop_sin), + ( ( sign_im && !mpfr_signbit(c)) + || (!sign_im && mpfr_signbit(c)) ? -1 : 1)); + } + + if (rop_cos != NULL) { + mpfr_set (mpc_realref (rop_cos), c, GMP_RNDN); /* exact */ + inex_cos_re = inex_c; + mpfr_set_zero (mpc_imagref (rop_cos), + ( ( sign_im && mpfr_signbit(s)) + || (!sign_im && !mpfr_signbit(s)) ? -1 : 1)); + } + + mpfr_clear (s); + mpfr_clear (c); + + return MPC_INEX12 (MPC_INEX (inex_sin_re, 0), MPC_INEX (inex_cos_re, 0)); +} + + +static int +mpc_sin_cos_imag (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, + mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) + /* assumes that op is purely imaginary, but not zero */ +{ + int inex_sin_im = 0, inex_cos_re = 0; + /* assume exact if not computed */ + int overlap; + mpc_t op_loc; + + overlap = (rop_sin == op || rop_cos == op); + if (overlap) { + mpc_init3 (op_loc, MPC_PREC_RE (op), MPC_PREC_IM (op)); + mpc_set (op_loc, op, MPC_RNDNN); + } + else + op_loc [0] = op [0]; + + if (rop_sin != NULL) { + /* sin(+-O +i*y) = +-0 +i*sinh(y) */ + mpfr_set (mpc_realref(rop_sin), mpc_realref(op_loc), GMP_RNDN); + inex_sin_im = mpfr_sinh (mpc_imagref(rop_sin), mpc_imagref(op_loc), MPC_RND_IM(rnd_sin)); + } + + if (rop_cos != NULL) { + /* cos(-0 - i * y) = cos(+0 + i * y) = cosh(y) - i * 0, + cos(-0 + i * y) = cos(+0 - i * y) = cosh(y) + i * 0, + where y > 0 */ + inex_cos_re = mpfr_cosh (mpc_realref (rop_cos), mpc_imagref (op_loc), MPC_RND_RE (rnd_cos)); + + mpfr_set_ui (mpc_imagref (rop_cos), 0ul, MPC_RND_IM (rnd_cos)); + if (mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc))) + MPFR_CHANGE_SIGN (mpc_imagref (rop_cos)); + } + + if (overlap) + mpc_clear (op_loc); + + return MPC_INEX12 (MPC_INEX (0, inex_sin_im), MPC_INEX (inex_cos_re, 0)); +} + + +int +mpc_sin_cos (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op, + mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos) + /* Feature not documented in the texinfo file: One of rop_sin or + rop_cos may be NULL, in which case it is not computed, and the + corresponding ternary inexact value is set to 0 (exact). */ +{ + if (!mpc_fin_p (op)) + return mpc_sin_cos_nonfinite (rop_sin, rop_cos, op, rnd_sin, rnd_cos); + else if (mpfr_zero_p (mpc_imagref (op))) + return mpc_sin_cos_real (rop_sin, rop_cos, op, rnd_sin, rnd_cos); + else if (mpfr_zero_p (mpc_realref (op))) + return mpc_sin_cos_imag (rop_sin, rop_cos, op, rnd_sin, rnd_cos); + else { + /* let op = a + i*b, then sin(op) = sin(a)*cosh(b) + i*cos(a)*sinh(b) + and cos(op) = cos(a)*cosh(b) - i*sin(a)*sinh(b). + + For Re(sin(op)) (and analogously, the other parts), we use the + following algorithm, with rounding to nearest for all operations + and working precision w: + + (1) x = o(sin(a)) + (2) y = o(cosh(b)) + (3) r = o(x*y) + then the error on r is at most 4 ulps, since we can write + r = sin(a)*cosh(b)*(1+t)^3 with |t| <= 2^(-w), + thus for w >= 2, r = sin(a)*cosh(b)*(1+4*t) with |t| <= 2^(-w), + thus the relative error is bounded by 4*2^(-w) <= 4*ulp(r). + */ + mpfr_t s, c, sh, ch, sch, csh; + mpfr_prec_t prec; + int ok; + int inex_re, inex_im, inex_sin, inex_cos; + + prec = 2; + if (rop_sin != NULL) + prec = MPC_MAX (prec, MPC_MAX_PREC (rop_sin)); + if (rop_cos != NULL) + prec = MPC_MAX (prec, MPC_MAX_PREC (rop_cos)); + + mpfr_init2 (s, 2); + mpfr_init2 (c, 2); + mpfr_init2 (sh, 2); + mpfr_init2 (ch, 2); + mpfr_init2 (sch, 2); + mpfr_init2 (csh, 2); + + do { + ok = 1; + prec += mpc_ceil_log2 (prec) + 5; + + mpfr_set_prec (s, prec); + mpfr_set_prec (c, prec); + mpfr_set_prec (sh, prec); + mpfr_set_prec (ch, prec); + mpfr_set_prec (sch, prec); + mpfr_set_prec (csh, prec); + + mpfr_sin_cos (s, c, mpc_realref(op), GMP_RNDN); + mpfr_sinh_cosh (sh, ch, mpc_imagref(op), GMP_RNDN); + + if (rop_sin != NULL) { + /* real part of sine */ + mpfr_mul (sch, s, ch, GMP_RNDN); + ok = (!mpfr_number_p (sch)) + || mpfr_can_round (sch, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE (rop_sin) + + (MPC_RND_RE (rnd_sin) == GMP_RNDN)); + + if (ok) { + /* imaginary part of sine */ + mpfr_mul (csh, c, sh, GMP_RNDN); + ok = (!mpfr_number_p (csh)) + || mpfr_can_round (csh, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM (rop_sin) + + (MPC_RND_IM (rnd_sin) == GMP_RNDN)); + } + } + + if (rop_cos != NULL && ok) { + /* real part of cosine */ + mpfr_mul (c, c, ch, GMP_RNDN); + ok = (!mpfr_number_p (c)) + || mpfr_can_round (c, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE (rop_cos) + + (MPC_RND_RE (rnd_cos) == GMP_RNDN)); + + if (ok) { + /* imaginary part of cosine */ + mpfr_mul (s, s, sh, GMP_RNDN); + mpfr_neg (s, s, GMP_RNDN); + ok = (!mpfr_number_p (s)) + || mpfr_can_round (s, prec - 2, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM (rop_cos) + + (MPC_RND_IM (rnd_cos) == GMP_RNDN)); + } + } + } while (ok == 0); + + if (rop_sin != NULL) { + inex_re = mpfr_set (mpc_realref (rop_sin), sch, MPC_RND_RE (rnd_sin)); + if (mpfr_inf_p (sch)) + inex_re = mpfr_sgn (sch); + inex_im = mpfr_set (mpc_imagref (rop_sin), csh, MPC_RND_IM (rnd_sin)); + if (mpfr_inf_p (csh)) + inex_im = mpfr_sgn (csh); + inex_sin = MPC_INEX (inex_re, inex_im); + } + else + inex_sin = MPC_INEX (0,0); /* return exact if not computed */ + + if (rop_cos != NULL) { + inex_re = mpfr_set (mpc_realref (rop_cos), c, MPC_RND_RE (rnd_cos)); + if (mpfr_inf_p (c)) + inex_re = mpfr_sgn (c); + inex_im = mpfr_set (mpc_imagref (rop_cos), s, MPC_RND_IM (rnd_cos)); + if (mpfr_inf_p (s)) + inex_im = mpfr_sgn (s); + inex_cos = MPC_INEX (inex_re, inex_im); + } + else + inex_cos = MPC_INEX (0,0); /* return exact if not computed */ + + mpfr_clear (s); + mpfr_clear (c); + mpfr_clear (sh); + mpfr_clear (ch); + mpfr_clear (sch); + mpfr_clear (csh); + + return (MPC_INEX12 (inex_sin, inex_cos)); + } +} diff --git a/mpc/src/sinh.c b/mpc/src/sinh.c new file mode 100644 index 0000000000..509cc57dea --- /dev/null +++ b/mpc/src/sinh.c @@ -0,0 +1,47 @@ +/* mpc_sinh -- hyperbolic sine of a complex number. + +Copyright (C)2008, 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_sinh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* sinh(op) = -i*sin(i*op) = conj(-i*sin(conj(-i*op))) */ + mpc_t z; + mpc_t sin_z; + int inex; + + /* z := conj(-i * op) and rop = conj(-i * sin(z)), in other words, we have + to switch real and imaginary parts. Let us set them without copying + significands. */ + mpc_realref (z)[0] = mpc_imagref (op)[0]; + mpc_imagref (z)[0] = mpc_realref (op)[0]; + mpc_realref (sin_z)[0] = mpc_imagref (rop)[0]; + mpc_imagref (sin_z)[0] = mpc_realref (rop)[0]; + + inex = mpc_sin (sin_z, z, MPC_RND (MPC_RND_IM (rnd), MPC_RND_RE (rnd))); + + /* sin_z and rop parts share the same significands, copy the rest now. */ + mpc_realref (rop)[0] = mpc_imagref (sin_z)[0]; + mpc_imagref (rop)[0] = mpc_realref (sin_z)[0]; + + /* swap inexact flags for real and imaginary parts */ + return MPC_INEX (MPC_INEX_IM (inex), MPC_INEX_RE (inex)); +} diff --git a/mpc/src/sqr.c b/mpc/src/sqr.c new file mode 100644 index 0000000000..f1ce1bac8a --- /dev/null +++ b/mpc/src/sqr.c @@ -0,0 +1,324 @@ +/* mpc_sqr -- Square a complex number. + +Copyright (C) 2002, 2005, 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include "mpc-impl.h" + + +static int +mpfr_fsss (mpfr_ptr z, mpfr_srcptr a, mpfr_srcptr c, mpfr_rnd_t rnd) +{ + /* Computes z = a^2 - c^2. + Assumes that a and c are finite and non-zero; so a squaring yielding + an infinity is an overflow, and a squaring yielding 0 is an underflow. + Assumes further that z is distinct from a and c. */ + + int inex; + mpfr_t u, v; + + /* u=a^2, v=c^2 exactly */ + mpfr_init2 (u, 2*mpfr_get_prec (a)); + mpfr_init2 (v, 2*mpfr_get_prec (c)); + mpfr_sqr (u, a, GMP_RNDN); + mpfr_sqr (v, c, GMP_RNDN); + + /* tentatively compute z as u-v; here we need z to be distinct + from a and c to not lose the latter */ + inex = mpfr_sub (z, u, v, rnd); + + if (mpfr_inf_p (z)) { + /* replace by "correctly rounded overflow" */ + mpfr_set_si (z, (mpfr_signbit (z) ? -1 : 1), GMP_RNDN); + inex = mpfr_mul_2ui (z, z, mpfr_get_emax (), rnd); + } + else if (mpfr_zero_p (u) && !mpfr_zero_p (v)) { + /* exactly u underflowed, determine inexact flag */ + inex = (mpfr_signbit (u) ? 1 : -1); + } + else if (mpfr_zero_p (v) && !mpfr_zero_p (u)) { + /* exactly v underflowed, determine inexact flag */ + inex = (mpfr_signbit (v) ? -1 : 1); + } + else if (mpfr_nan_p (z) || (mpfr_zero_p (u) && mpfr_zero_p (v))) { + /* In the first case, u and v are +inf. + In the second case, u and v are zeroes; their difference may be 0 + or the least representable number, with a sign to be determined. + Redo the computations with mpz_t exponents */ + mpfr_exp_t ea, ec; + mpz_t eu, ev; + /* cheat to work around the const qualifiers */ + + /* Normalise the input by shifting and keep track of the shifts in + the exponents of u and v */ + ea = mpfr_get_exp (a); + ec = mpfr_get_exp (c); + + mpfr_set_exp ((mpfr_ptr) a, (mpfr_prec_t) 0); + mpfr_set_exp ((mpfr_ptr) c, (mpfr_prec_t) 0); + + mpz_init (eu); + mpz_init (ev); + mpz_set_si (eu, (long int) ea); + mpz_mul_2exp (eu, eu, 1); + mpz_set_si (ev, (long int) ec); + mpz_mul_2exp (ev, ev, 1); + + /* recompute u and v and move exponents to eu and ev */ + mpfr_sqr (u, a, GMP_RNDN); + /* exponent of u is non-positive */ + mpz_sub_ui (eu, eu, (unsigned long int) (-mpfr_get_exp (u))); + mpfr_set_exp (u, (mpfr_prec_t) 0); + mpfr_sqr (v, c, GMP_RNDN); + mpz_sub_ui (ev, ev, (unsigned long int) (-mpfr_get_exp (v))); + mpfr_set_exp (v, (mpfr_prec_t) 0); + if (mpfr_nan_p (z)) { + mpfr_exp_t emax = mpfr_get_emax (); + int overflow; + /* We have a = ma * 2^ea with 1/2 <= |ma| < 1 and ea <= emax. + So eu <= 2*emax, and eu > emax since we have + an overflow. The same holds for ev. Shift u and v by as much as + possible so that one of them has exponent emax and the + remaining exponents in eu and ev are the same. Then carry out + the addition. Shifting u and v prevents an underflow. */ + if (mpz_cmp (eu, ev) >= 0) { + mpfr_set_exp (u, emax); + mpz_sub_ui (eu, eu, (long int) emax); + mpz_sub (ev, ev, eu); + mpfr_set_exp (v, (mpfr_exp_t) mpz_get_ui (ev)); + /* remaining common exponent is now in eu */ + } + else { + mpfr_set_exp (v, emax); + mpz_sub_ui (ev, ev, (long int) emax); + mpz_sub (eu, eu, ev); + mpfr_set_exp (u, (mpfr_exp_t) mpz_get_ui (eu)); + mpz_set (eu, ev); + /* remaining common exponent is now also in eu */ + } + inex = mpfr_sub (z, u, v, rnd); + /* Result is finite since u and v have the same sign. */ + overflow = mpfr_mul_2ui (z, z, mpz_get_ui (eu), rnd); + if (overflow) + inex = overflow; + } + else { + int underflow; + /* Subtraction of two zeroes. We have a = ma * 2^ea + with 1/2 <= |ma| < 1 and ea >= emin and similarly for b. + So 2*emin < 2*emin+1 <= eu < emin < 0, and analogously for v. */ + mpfr_exp_t emin = mpfr_get_emin (); + if (mpz_cmp (eu, ev) <= 0) { + mpfr_set_exp (u, emin); + mpz_add_ui (eu, eu, (unsigned long int) (-emin)); + mpz_sub (ev, ev, eu); + mpfr_set_exp (v, (mpfr_exp_t) mpz_get_si (ev)); + } + else { + mpfr_set_exp (v, emin); + mpz_add_ui (ev, ev, (unsigned long int) (-emin)); + mpz_sub (eu, eu, ev); + mpfr_set_exp (u, (mpfr_exp_t) mpz_get_si (eu)); + mpz_set (eu, ev); + } + inex = mpfr_sub (z, u, v, rnd); + mpz_neg (eu, eu); + underflow = mpfr_div_2ui (z, z, mpz_get_ui (eu), rnd); + if (underflow) + inex = underflow; + } + + mpz_clear (eu); + mpz_clear (ev); + + mpfr_set_exp ((mpfr_ptr) a, ea); + mpfr_set_exp ((mpfr_ptr) c, ec); + /* works also when a == c */ + } + + mpfr_clear (u); + mpfr_clear (v); + + return inex; +} + + +int +mpc_sqr (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + int ok; + mpfr_t u, v; + mpfr_t x; + /* temporary variable to hold the real part of op, + needed in the case rop==op */ + mpfr_prec_t prec; + int inex_re, inex_im, inexact; + mpfr_exp_t emin; + int saved_underflow; + + /* special values: NaN and infinities */ + if (!mpc_fin_p (op)) { + if (mpfr_nan_p (mpc_realref (op)) || mpfr_nan_p (mpc_imagref (op))) { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + } + else if (mpfr_inf_p (mpc_realref (op))) { + if (mpfr_inf_p (mpc_imagref (op))) { + mpfr_set_inf (mpc_imagref (rop), + MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); + mpfr_set_nan (mpc_realref (rop)); + } + else { + if (mpfr_zero_p (mpc_imagref (op))) + mpfr_set_nan (mpc_imagref (rop)); + else + mpfr_set_inf (mpc_imagref (rop), + MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); + mpfr_set_inf (mpc_realref (rop), +1); + } + } + else /* IM(op) is infinity, RE(op) is not */ { + if (mpfr_zero_p (mpc_realref (op))) + mpfr_set_nan (mpc_imagref (rop)); + else + mpfr_set_inf (mpc_imagref (rop), + MPFR_SIGN (mpc_realref (op)) * MPFR_SIGN (mpc_imagref (op))); + mpfr_set_inf (mpc_realref (rop), -1); + } + return MPC_INEX (0, 0); /* exact */ + } + + prec = MPC_MAX_PREC(rop); + + /* Check for real resp. purely imaginary number */ + if (mpfr_zero_p (mpc_imagref(op))) { + int same_sign = mpfr_signbit (mpc_realref (op)) == mpfr_signbit (mpc_imagref (op)); + inex_re = mpfr_sqr (mpc_realref(rop), mpc_realref(op), MPC_RND_RE(rnd)); + inex_im = mpfr_set_ui (mpc_imagref(rop), 0ul, GMP_RNDN); + if (!same_sign) + mpc_conj (rop, rop, MPC_RNDNN); + return MPC_INEX(inex_re, inex_im); + } + if (mpfr_zero_p (mpc_realref(op))) { + int same_sign = mpfr_signbit (mpc_realref (op)) == mpfr_signbit (mpc_imagref (op)); + inex_re = -mpfr_sqr (mpc_realref(rop), mpc_imagref(op), INV_RND (MPC_RND_RE(rnd))); + mpfr_neg (mpc_realref(rop), mpc_realref(rop), GMP_RNDN); + inex_im = mpfr_set_ui (mpc_imagref(rop), 0ul, GMP_RNDN); + if (!same_sign) + mpc_conj (rop, rop, MPC_RNDNN); + return MPC_INEX(inex_re, inex_im); + } + + if (rop == op) + { + mpfr_init2 (x, MPC_PREC_RE (op)); + mpfr_set (x, op->re, GMP_RNDN); + } + else + x [0] = op->re [0]; + /* From here on, use x instead of op->re and safely overwrite rop->re. */ + + /* Compute real part of result. */ + if (SAFE_ABS (mpfr_exp_t, + mpfr_get_exp (mpc_realref (op)) - mpfr_get_exp (mpc_imagref (op))) + > (mpfr_exp_t) MPC_MAX_PREC (op) / 2) { + /* If the real and imaginary parts of the argument have very different + exponents, it is not reasonable to use Karatsuba squaring; compute + exactly with the standard formulae instead, even if this means an + additional multiplication. Using the approach copied from mul, over- + and underflows are also handled correctly. */ + + inex_re = mpfr_fsss (rop->re, x, op->im, MPC_RND_RE (rnd)); + } + else { + /* Karatsuba squaring: we compute the real part as (x+y)*(x-y) and the + imaginary part as 2*x*y, with a total of 2M instead of 2S+1M for the + naive algorithm, which computes x^2-y^2 and 2*y*y */ + mpfr_init (u); + mpfr_init (v); + + emin = mpfr_get_emin (); + + do + { + prec += mpc_ceil_log2 (prec) + 5; + + mpfr_set_prec (u, prec); + mpfr_set_prec (v, prec); + + /* Let op = x + iy. We need u = x+y and v = x-y, rounded away. */ + /* The error is bounded above by 1 ulp. */ + /* We first let inexact be 1 if the real part is not computed */ + /* exactly and determine the sign later. */ + inexact = ROUND_AWAY (mpfr_add (u, x, mpc_imagref (op), MPFR_RNDA), u) + | ROUND_AWAY (mpfr_sub (v, x, mpc_imagref (op), MPFR_RNDA), v); + + /* compute the real part as u*v, rounded away */ + /* determine also the sign of inex_re */ + + if (mpfr_sgn (u) == 0 || mpfr_sgn (v) == 0) { + /* as we have rounded away, the result is exact */ + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + inex_re = 0; + ok = 1; + } + else { + mpfr_rnd_t rnd_away; + /* FIXME: can be replaced by MPFR_RNDA in mpfr >= 3 */ + rnd_away = (mpfr_sgn (u) * mpfr_sgn (v) > 0 ? GMP_RNDU : GMP_RNDD); + inexact |= ROUND_AWAY (mpfr_mul (u, u, v, MPFR_RNDA), u); /* error 5 */ + if (mpfr_get_exp (u) == emin || mpfr_inf_p (u)) { + /* under- or overflow */ + inex_re = mpfr_fsss (rop->re, x, op->im, MPC_RND_RE (rnd)); + ok = 1; + } + else { + ok = (!inexact) | mpfr_can_round (u, prec - 3, + rnd_away, GMP_RNDZ, + MPC_PREC_RE (rop) + (MPC_RND_RE (rnd) == GMP_RNDN)); + if (ok) { + inex_re = mpfr_set (mpc_realref (rop), u, MPC_RND_RE (rnd)); + if (inex_re == 0) + /* remember that u was already rounded */ + inex_re = inexact; + } + } + } + } + while (!ok); + + mpfr_clear (u); + mpfr_clear (v); + } + + saved_underflow = mpfr_underflow_p (); + mpfr_clear_underflow (); + inex_im = mpfr_mul (rop->im, x, op->im, MPC_RND_IM (rnd)); + if (!mpfr_underflow_p ()) + inex_im |= mpfr_mul_2ui (rop->im, rop->im, 1, MPC_RND_IM (rnd)); + /* We must not multiply by 2 if rop->im has been set to the smallest + representable number. */ + if (saved_underflow) + mpfr_set_underflow (); + + if (rop == op) + mpfr_clear (x); + + return MPC_INEX (inex_re, inex_im); +} diff --git a/mpc/src/sqrt.c b/mpc/src/sqrt.c new file mode 100644 index 0000000000..dd2ff60034 --- /dev/null +++ b/mpc/src/sqrt.c @@ -0,0 +1,364 @@ +/* mpc_sqrt -- Take the square root of a complex number. + +Copyright (C) 2002, 2008, 2009, 2010, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +#if MPFR_VERSION_MAJOR < 3 +#define mpfr_min_prec(x) \ + ( ((prec + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) * BITS_PER_MP_LIMB \ + - mpn_scan1 (x->_mpfr_d, 0)) +#endif + + +int +mpc_sqrt (mpc_ptr a, mpc_srcptr b, mpc_rnd_t rnd) +{ + int ok_w, ok_t = 0; + mpfr_t w, t; + mpfr_rnd_t rnd_w, rnd_t; + mpfr_prec_t prec_w, prec_t; + /* the rounding mode and the precision required for w and t, which can */ + /* be either the real or the imaginary part of a */ + mpfr_prec_t prec; + int inex_w, inex_t = 1, inex_re, inex_im, loops = 0; + const int re_cmp = mpfr_cmp_ui (mpc_realref (b), 0), + im_cmp = mpfr_cmp_ui (mpc_imagref (b), 0); + /* comparison of the real/imaginary part of b with 0 */ + int repr_w, repr_t = 0 /* to avoid gcc warning */ ; + /* flag indicating whether the computed value is already representable + at the target precision */ + const int im_sgn = mpfr_signbit (mpc_imagref (b)) == 0 ? 0 : -1; + /* we need to know the sign of Im(b) when it is +/-0 */ + const mpfr_rnd_t r = im_sgn ? GMP_RNDD : GMP_RNDU; + /* rounding mode used when computing t */ + + /* special values */ + if (!mpc_fin_p (b)) { + /* sqrt(x +i*Inf) = +Inf +I*Inf, even if x = NaN */ + /* sqrt(x -i*Inf) = +Inf -I*Inf, even if x = NaN */ + if (mpfr_inf_p (mpc_imagref (b))) + { + mpfr_set_inf (mpc_realref (a), +1); + mpfr_set_inf (mpc_imagref (a), im_sgn); + return MPC_INEX (0, 0); + } + + if (mpfr_inf_p (mpc_realref (b))) + { + if (mpfr_signbit (mpc_realref (b))) + { + if (mpfr_number_p (mpc_imagref (b))) + { + /* sqrt(-Inf +i*y) = +0 +i*Inf, when y positive */ + /* sqrt(-Inf +i*y) = +0 -i*Inf, when y positive */ + mpfr_set_ui (mpc_realref (a), 0, GMP_RNDN); + mpfr_set_inf (mpc_imagref (a), im_sgn); + return MPC_INEX (0, 0); + } + else + { + /* sqrt(-Inf +i*NaN) = NaN +/-i*Inf */ + mpfr_set_nan (mpc_realref (a)); + mpfr_set_inf (mpc_imagref (a), im_sgn); + return MPC_INEX (0, 0); + } + } + else + { + if (mpfr_number_p (mpc_imagref (b))) + { + /* sqrt(+Inf +i*y) = +Inf +i*0, when y positive */ + /* sqrt(+Inf +i*y) = +Inf -i*0, when y positive */ + mpfr_set_inf (mpc_realref (a), +1); + mpfr_set_ui (mpc_imagref (a), 0, GMP_RNDN); + if (im_sgn) + mpc_conj (a, a, MPC_RNDNN); + return MPC_INEX (0, 0); + } + else + { + /* sqrt(+Inf -i*Inf) = +Inf -i*Inf */ + /* sqrt(+Inf +i*Inf) = +Inf +i*Inf */ + /* sqrt(+Inf +i*NaN) = +Inf +i*NaN */ + return mpc_set (a, b, rnd); + } + } + } + + /* sqrt(x +i*NaN) = NaN +i*NaN, if x is not infinite */ + /* sqrt(NaN +i*y) = NaN +i*NaN, if y is not infinite */ + if (mpfr_nan_p (mpc_realref (b)) || mpfr_nan_p (mpc_imagref (b))) + { + mpfr_set_nan (mpc_realref (a)); + mpfr_set_nan (mpc_imagref (a)); + return MPC_INEX (0, 0); + } + } + + /* purely real */ + if (im_cmp == 0) + { + if (re_cmp == 0) + { + mpc_set_ui_ui (a, 0, 0, MPC_RNDNN); + if (im_sgn) + mpc_conj (a, a, MPC_RNDNN); + return MPC_INEX (0, 0); + } + else if (re_cmp > 0) + { + inex_w = mpfr_sqrt (mpc_realref (a), mpc_realref (b), MPC_RND_RE (rnd)); + mpfr_set_ui (mpc_imagref (a), 0, GMP_RNDN); + if (im_sgn) + mpc_conj (a, a, MPC_RNDNN); + return MPC_INEX (inex_w, 0); + } + else + { + mpfr_init2 (w, MPC_PREC_RE (b)); + mpfr_neg (w, mpc_realref (b), GMP_RNDN); + if (im_sgn) + { + inex_w = -mpfr_sqrt (mpc_imagref (a), w, INV_RND (MPC_RND_IM (rnd))); + mpfr_neg (mpc_imagref (a), mpc_imagref (a), GMP_RNDN); + } + else + inex_w = mpfr_sqrt (mpc_imagref (a), w, MPC_RND_IM (rnd)); + + mpfr_set_ui (mpc_realref (a), 0, GMP_RNDN); + mpfr_clear (w); + return MPC_INEX (0, inex_w); + } + } + + /* purely imaginary */ + if (re_cmp == 0) + { + mpfr_t y; + + y[0] = mpc_imagref (b)[0]; + /* If y/2 underflows, so does sqrt(y/2) */ + mpfr_div_2ui (y, y, 1, GMP_RNDN); + if (im_cmp > 0) + { + inex_w = mpfr_sqrt (mpc_realref (a), y, MPC_RND_RE (rnd)); + inex_t = mpfr_sqrt (mpc_imagref (a), y, MPC_RND_IM (rnd)); + } + else + { + mpfr_neg (y, y, GMP_RNDN); + inex_w = mpfr_sqrt (mpc_realref (a), y, MPC_RND_RE (rnd)); + inex_t = -mpfr_sqrt (mpc_imagref (a), y, INV_RND (MPC_RND_IM (rnd))); + mpfr_neg (mpc_imagref (a), mpc_imagref (a), GMP_RNDN); + } + return MPC_INEX (inex_w, inex_t); + } + + prec = MPC_MAX_PREC(a); + + mpfr_init (w); + mpfr_init (t); + + if (re_cmp > 0) { + rnd_w = MPC_RND_RE (rnd); + prec_w = MPC_PREC_RE (a); + rnd_t = MPC_RND_IM(rnd); + if (rnd_t == GMP_RNDZ) + /* force GMP_RNDD or GMP_RNDUP, using sign(t) = sign(y) */ + rnd_t = (im_cmp > 0 ? GMP_RNDD : GMP_RNDU); + prec_t = MPC_PREC_IM (a); + } + else { + prec_w = MPC_PREC_IM (a); + prec_t = MPC_PREC_RE (a); + if (im_cmp > 0) { + rnd_w = MPC_RND_IM(rnd); + rnd_t = MPC_RND_RE(rnd); + if (rnd_t == GMP_RNDZ) + rnd_t = GMP_RNDD; + } + else { + rnd_w = INV_RND(MPC_RND_IM (rnd)); + rnd_t = INV_RND(MPC_RND_RE (rnd)); + if (rnd_t == GMP_RNDZ) + rnd_t = GMP_RNDU; + } + } + + do + { + loops ++; + prec += (loops <= 2) ? mpc_ceil_log2 (prec) + 4 : prec / 2; + mpfr_set_prec (w, prec); + mpfr_set_prec (t, prec); + /* let b = x + iy */ + /* w = sqrt ((|x| + sqrt (x^2 + y^2)) / 2), rounded down */ + /* total error bounded by 3 ulps */ + inex_w = mpc_abs (w, b, GMP_RNDD); + if (re_cmp < 0) + inex_w |= mpfr_sub (w, w, mpc_realref (b), GMP_RNDD); + else + inex_w |= mpfr_add (w, w, mpc_realref (b), GMP_RNDD); + inex_w |= mpfr_div_2ui (w, w, 1, GMP_RNDD); + inex_w |= mpfr_sqrt (w, w, GMP_RNDD); + + repr_w = mpfr_min_prec (w) <= prec_w; + if (!repr_w) + /* use the usual trick for obtaining the ternary value */ + ok_w = mpfr_can_round (w, prec - 2, GMP_RNDD, GMP_RNDU, + prec_w + (rnd_w == GMP_RNDN)); + else { + /* w is representable in the target precision and thus cannot be + rounded up */ + if (rnd_w == GMP_RNDN) + /* If w can be rounded to nearest, then actually no rounding + occurs, and the ternary value is known from inex_w. */ + ok_w = mpfr_can_round (w, prec - 2, GMP_RNDD, GMP_RNDN, prec_w); + else + /* If w can be rounded down, then any direct rounding and the + ternary flag can be determined from inex_w. */ + ok_w = mpfr_can_round (w, prec - 2, GMP_RNDD, GMP_RNDD, prec_w); + } + + if (!inex_w || ok_w) { + /* t = y / 2w, rounded away */ + /* total error bounded by 7 ulps */ + inex_t = mpfr_div (t, mpc_imagref (b), w, r); + if (!inex_t && inex_w) + /* The division was exact, but w was not. */ + inex_t = im_sgn ? -1 : 1; + inex_t |= mpfr_div_2ui (t, t, 1, r); + repr_t = mpfr_min_prec (t) <= prec_t; + if (!repr_t) + /* As for w; since t was rounded away, we check whether rounding to 0 + is possible. */ + ok_t = mpfr_can_round (t, prec - 3, r, GMP_RNDZ, + prec_t + (rnd_t == GMP_RNDN)); + else { + if (rnd_t == GMP_RNDN) + ok_t = mpfr_can_round (t, prec - 3, r, GMP_RNDN, prec_t); + else + ok_t = mpfr_can_round (t, prec - 3, r, r, prec_t); + } + } + } + while ((inex_w && !ok_w) || (inex_t && !ok_t)); + + if (re_cmp > 0) { + inex_re = mpfr_set (mpc_realref (a), w, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref (a), t, MPC_RND_IM(rnd)); + } + else if (im_cmp > 0) { + inex_re = mpfr_set (mpc_realref(a), t, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), w, MPC_RND_IM(rnd)); + } + else { + inex_re = mpfr_neg (mpc_realref (a), t, MPC_RND_RE(rnd)); + inex_im = mpfr_neg (mpc_imagref (a), w, MPC_RND_IM(rnd)); + } + + if (repr_w && inex_w) { + if (rnd_w == GMP_RNDN) { + /* w has not been rounded with mpfr_set/mpfr_neg, determine ternary + value from inex_w instead */ + if (re_cmp > 0) + inex_re = inex_w; + else if (im_cmp > 0) + inex_im = inex_w; + else + inex_im = -inex_w; + } + else { + /* determine ternary value, but also potentially add 1 ulp; can only + be done now when we are in the target precision */ + if (re_cmp > 0) { + if (rnd_w == GMP_RNDU) { + MPFR_ADD_ONE_ULP (mpc_realref (a)); + inex_re = +1; + } + else + inex_re = -1; + } + else if (im_cmp > 0) { + if (rnd_w == GMP_RNDU) { + MPFR_ADD_ONE_ULP (mpc_imagref (a)); + inex_im = +1; + } + else + inex_im = -1; + } + else { + if (rnd_w == GMP_RNDU) { + MPFR_ADD_ONE_ULP (mpc_imagref (a)); + inex_im = -1; + } + else + inex_im = +1; + } + } + } + if (repr_t && inex_t) { + if (rnd_t == GMP_RNDN) { + if (re_cmp > 0) + inex_im = inex_t; + else if (im_cmp > 0) + inex_re = inex_t; + else + inex_re = -inex_t; + } + else { + if (re_cmp > 0) { + if (rnd_t == r) + inex_im = inex_t; + else { + inex_im = -inex_t; + /* im_cmp > 0 implies that Im(b) > 0, thus im_sgn = 0 + and r = GMP_RNDU. + im_cmp < 0 implies that Im(b) < 0, thus im_sgn = -1 + and r = GMP_RNDD. */ + MPFR_SUB_ONE_ULP (mpc_imagref (a)); + } + } + else if (im_cmp > 0) { + if (rnd_t == r) + inex_re = inex_t; + else { + inex_re = -inex_t; + /* im_cmp > 0 implies r = GMP_RNDU (see above) */ + MPFR_SUB_ONE_ULP (mpc_realref (a)); + } + } + else { /* im_cmp < 0 */ + if (rnd_t == r) + inex_re = -inex_t; + else { + inex_re = inex_t; + /* im_cmp < 0 implies r = GMP_RNDD (see above) */ + MPFR_SUB_ONE_ULP (mpc_realref (a)); + } + } + } + } + + mpfr_clear (w); + mpfr_clear (t); + + return MPC_INEX (inex_re, inex_im); +} diff --git a/mpc/src/strtoc.c b/mpc/src/strtoc.c new file mode 100644 index 0000000000..b96ccee528 --- /dev/null +++ b/mpc/src/strtoc.c @@ -0,0 +1,89 @@ +/* mpc_strtoc -- Read a complex number from a string. + +Copyright (C) 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <string.h> +#include <ctype.h> +#include "mpc-impl.h" + +static void +skip_whitespace (const char **p) +{ + /* TODO: This function had better be inlined, but it is unclear whether + the hassle to get this implemented across all platforms is worth it. */ + while (isspace ((unsigned char) **p)) + (*p)++; +} + +int +mpc_strtoc (mpc_ptr rop, const char *nptr, char **endptr, int base, mpc_rnd_t rnd) +{ + const char *p; + char *end; + int bracketed = 0; + + int inex_re = 0, inex_im = 0; + + if (nptr == NULL || base > 36 || base == 1) + goto error; + + p = nptr; + skip_whitespace (&p); + + if (*p == '('){ + bracketed = 1; + ++p; + } + + inex_re = mpfr_strtofr (mpc_realref(rop), p, &end, base, MPC_RND_RE (rnd)); + if (end == p) + goto error; + p = end; + + if (!bracketed) + inex_im = mpfr_set_ui (mpc_imagref (rop), 0ul, GMP_RNDN); + else { + if (!isspace ((unsigned char)*p)) + goto error; + + skip_whitespace (&p); + + inex_im = mpfr_strtofr (mpc_imagref(rop), p, &end, base, MPC_RND_IM (rnd)); + if (end == p) + goto error; + p = end; + + skip_whitespace (&p); + if (*p != ')') + goto error; + + p++; + } + + if (endptr != NULL) + *endptr = (char*) p; + return MPC_INEX (inex_re, inex_im); + +error: + if (endptr != NULL) + *endptr = (char*) nptr; + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + return -1; +} diff --git a/mpc/src/sub.c b/mpc/src/sub.c new file mode 100644 index 0000000000..8b8a97d24f --- /dev/null +++ b/mpc/src/sub.c @@ -0,0 +1,32 @@ +/* mpc_sub -- Subtract two complex numbers. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_sub (mpc_ptr a, mpc_srcptr b, mpc_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_sub (mpc_realref(a), mpc_realref(b), mpc_realref(c), MPC_RND_RE(rnd)); + inex_im = mpfr_sub (mpc_imagref(a), mpc_imagref(b), mpc_imagref(c), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/sub_fr.c b/mpc/src/sub_fr.c new file mode 100644 index 0000000000..ade33a95cd --- /dev/null +++ b/mpc/src/sub_fr.c @@ -0,0 +1,34 @@ +/* mpc_sub_fr -- Substract a floating-point number to the real part of a + complex number. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_sub_fr (mpc_ptr a, mpc_srcptr b, mpfr_srcptr c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_sub (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/sub_ui.c b/mpc/src/sub_ui.c new file mode 100644 index 0000000000..561c469676 --- /dev/null +++ b/mpc/src/sub_ui.c @@ -0,0 +1,33 @@ +/* mpc_sub_ui -- Add a complex number and an unsigned long int. + +Copyright (C) 2002, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* return 0 iff both the real and imaginary parts are exact */ +int +mpc_sub_ui (mpc_ptr a, mpc_srcptr b, unsigned long int c, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_sub_ui (mpc_realref(a), mpc_realref(b), c, MPC_RND_RE(rnd)); + inex_im = mpfr_set (mpc_imagref(a), mpc_imagref(b), MPC_RND_IM(rnd)); + + return MPC_INEX(inex_re, inex_im); +} diff --git a/mpc/src/swap.c b/mpc/src/swap.c new file mode 100644 index 0000000000..9590132e3b --- /dev/null +++ b/mpc/src/swap.c @@ -0,0 +1,29 @@ +/* mpc_swap -- Swap two complex numbers. + +Copyright (C) 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +void +mpc_swap (mpc_ptr a, mpc_ptr b) +{ + /* assumes real and imaginary parts do not overlap */ + mpfr_swap (mpc_realref(a), mpc_realref(b)); + mpfr_swap (mpc_imagref(a), mpc_imagref(b)); +} diff --git a/mpc/src/tan.c b/mpc/src/tan.c new file mode 100644 index 0000000000..24cd92b7ce --- /dev/null +++ b/mpc/src/tan.c @@ -0,0 +1,284 @@ +/* mpc_tan -- tangent of a complex number. + +Copyright (C) 2008, 2009, 2010, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <stdio.h> /* for MPC_ASSERT */ +#include <limits.h> +#include "mpc-impl.h" + +int +mpc_tan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + mpc_t x, y; + mpfr_prec_t prec; + mpfr_exp_t err; + int ok = 0; + int inex; + + /* special values */ + if (!mpc_fin_p (op)) + { + if (mpfr_nan_p (mpc_realref (op))) + { + if (mpfr_inf_p (mpc_imagref (op))) + /* tan(NaN -i*Inf) = +/-0 -i */ + /* tan(NaN +i*Inf) = +/-0 +i */ + { + /* exact unless 1 is not in exponent range */ + inex = mpc_set_si_si (rop, 0, + (MPFR_SIGN (mpc_imagref (op)) < 0) ? -1 : +1, + rnd); + } + else + /* tan(NaN +i*y) = NaN +i*NaN, when y is finite */ + /* tan(NaN +i*NaN) = NaN +i*NaN */ + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex = MPC_INEX (0, 0); /* always exact */ + } + } + else if (mpfr_nan_p (mpc_imagref (op))) + { + if (mpfr_cmp_ui (mpc_realref (op), 0) == 0) + /* tan(-0 +i*NaN) = -0 +i*NaN */ + /* tan(+0 +i*NaN) = +0 +i*NaN */ + { + mpc_set (rop, op, rnd); + inex = MPC_INEX (0, 0); /* always exact */ + } + else + /* tan(x +i*NaN) = NaN +i*NaN, when x != 0 */ + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex = MPC_INEX (0, 0); /* always exact */ + } + } + else if (mpfr_inf_p (mpc_realref (op))) + { + if (mpfr_inf_p (mpc_imagref (op))) + /* tan(-Inf -i*Inf) = -/+0 -i */ + /* tan(-Inf +i*Inf) = -/+0 +i */ + /* tan(+Inf -i*Inf) = +/-0 -i */ + /* tan(+Inf +i*Inf) = +/-0 +i */ + { + const int sign_re = mpfr_signbit (mpc_realref (op)); + int inex_im; + + mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd)); + mpfr_setsign (mpc_realref (rop), mpc_realref (rop), sign_re, GMP_RNDN); + + /* exact, unless 1 is not in exponent range */ + inex_im = mpfr_set_si (mpc_imagref (rop), + mpfr_signbit (mpc_imagref (op)) ? -1 : +1, + MPC_RND_IM (rnd)); + inex = MPC_INEX (0, inex_im); + } + else + /* tan(-Inf +i*y) = tan(+Inf +i*y) = NaN +i*NaN, when y is + finite */ + { + mpfr_set_nan (mpc_realref (rop)); + mpfr_set_nan (mpc_imagref (rop)); + inex = MPC_INEX (0, 0); /* always exact */ + } + } + else + /* tan(x -i*Inf) = +0*sin(x)*cos(x) -i, when x is finite */ + /* tan(x +i*Inf) = +0*sin(x)*cos(x) +i, when x is finite */ + { + mpfr_t c; + mpfr_t s; + int inex_im; + + mpfr_init (c); + mpfr_init (s); + + mpfr_sin_cos (s, c, mpc_realref (op), GMP_RNDN); + mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd)); + mpfr_setsign (mpc_realref (rop), mpc_realref (rop), + mpfr_signbit (c) != mpfr_signbit (s), GMP_RNDN); + /* exact, unless 1 is not in exponent range */ + inex_im = mpfr_set_si (mpc_imagref (rop), + (mpfr_signbit (mpc_imagref (op)) ? -1 : +1), + MPC_RND_IM (rnd)); + inex = MPC_INEX (0, inex_im); + + mpfr_clear (s); + mpfr_clear (c); + } + + return inex; + } + + if (mpfr_zero_p (mpc_realref (op))) + /* tan(-0 -i*y) = -0 +i*tanh(y), when y is finite. */ + /* tan(+0 +i*y) = +0 +i*tanh(y), when y is finite. */ + { + int inex_im; + + mpfr_set (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + inex_im = mpfr_tanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); + + return MPC_INEX (0, inex_im); + } + + if (mpfr_zero_p (mpc_imagref (op))) + /* tan(x -i*0) = tan(x) -i*0, when x is finite. */ + /* tan(x +i*0) = tan(x) +i*0, when x is finite. */ + { + int inex_re; + + inex_re = mpfr_tan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd)); + mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd)); + + return MPC_INEX (inex_re, 0); + } + + /* ordinary (non-zero) numbers */ + + /* tan(op) = sin(op) / cos(op). + + We use the following algorithm with rounding away from 0 for all + operations, and working precision w: + + (1) x = A(sin(op)) + (2) y = A(cos(op)) + (3) z = A(x/y) + + the error on Im(z) is at most 81 ulp, + the error on Re(z) is at most + 7 ulp if k < 2, + 8 ulp if k = 2, + else 5+k ulp, where + k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y)) + see proof in algorithms.tex. + */ + + prec = MPC_MAX_PREC(rop); + + mpc_init2 (x, 2); + mpc_init2 (y, 2); + + err = 7; + + do + { + mpfr_exp_t k, exr, eyr, eyi, ezr; + + ok = 0; + + /* FIXME: prevent addition overflow */ + prec += mpc_ceil_log2 (prec) + err; + mpc_set_prec (x, prec); + mpc_set_prec (y, prec); + + /* rounding away from zero: except in the cases x=0 or y=0 (processed + above), sin x and cos y are never exact, so rounding away from 0 is + rounding towards 0 and adding one ulp to the absolute value */ + mpc_sin_cos (x, y, op, MPC_RNDZZ, MPC_RNDZZ); + MPFR_ADD_ONE_ULP (mpc_realref (x)); + MPFR_ADD_ONE_ULP (mpc_imagref (x)); + MPFR_ADD_ONE_ULP (mpc_realref (y)); + MPFR_ADD_ONE_ULP (mpc_imagref (y)); + MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0); + + if ( mpfr_inf_p (mpc_realref (x)) || mpfr_inf_p (mpc_imagref (x)) + || mpfr_inf_p (mpc_realref (y)) || mpfr_inf_p (mpc_imagref (y))) { + /* If the real or imaginary part of x is infinite, it means that + Im(op) was large, in which case the result is + sign(tan(Re(op)))*0 + sign(Im(op))*I, + where sign(tan(Re(op))) = sign(Re(x))*sign(Re(y)). */ + int inex_re, inex_im; + mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN); + if (mpfr_sgn (mpc_realref (x)) * mpfr_sgn (mpc_realref (y)) < 0) + { + mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN); + inex_re = 1; + } + else + inex_re = -1; /* +0 is rounded down */ + if (mpfr_sgn (mpc_imagref (op)) > 0) + { + mpfr_set_ui (mpc_imagref (rop), 1, GMP_RNDN); + inex_im = 1; + } + else + { + mpfr_set_si (mpc_imagref (rop), -1, GMP_RNDN); + inex_im = -1; + } + inex = MPC_INEX(inex_re, inex_im); + goto end; + } + + exr = mpfr_get_exp (mpc_realref (x)); + eyr = mpfr_get_exp (mpc_realref (y)); + eyi = mpfr_get_exp (mpc_imagref (y)); + + /* some parts of the quotient may be exact */ + inex = mpc_div (x, x, y, MPC_RNDZZ); + /* OP is no pure real nor pure imaginary, so in theory the real and + imaginary parts of its tangent cannot be null. However due to + rouding errors this might happen. Consider for example + tan(1+14*I) = 1.26e-10 + 1.00*I. For small precision sin(op) and + cos(op) differ only by a factor I, thus after mpc_div x = I and + its real part is zero. */ + if (mpfr_zero_p (mpc_realref (x)) || mpfr_zero_p (mpc_imagref (x))) + { + err = prec; /* double precision */ + continue; + } + if (MPC_INEX_RE (inex)) + MPFR_ADD_ONE_ULP (mpc_realref (x)); + if (MPC_INEX_IM (inex)) + MPFR_ADD_ONE_ULP (mpc_imagref (x)); + MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0); + ezr = mpfr_get_exp (mpc_realref (x)); + + /* FIXME: compute + k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y)) + avoiding overflow */ + k = exr - ezr + MPC_MAX(-eyr, eyr - 2 * eyi); + err = k < 2 ? 7 : (k == 2 ? 8 : (5 + k)); + + /* Can the real part be rounded? */ + ok = (!mpfr_number_p (mpc_realref (x))) + || mpfr_can_round (mpc_realref(x), prec - err, GMP_RNDN, GMP_RNDZ, + MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == GMP_RNDN)); + + if (ok) + { + /* Can the imaginary part be rounded? */ + ok = (!mpfr_number_p (mpc_imagref (x))) + || mpfr_can_round (mpc_imagref(x), prec - 6, GMP_RNDN, GMP_RNDZ, + MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == GMP_RNDN)); + } + } + while (ok == 0); + + inex = mpc_set (rop, x, rnd); + + end: + mpc_clear (x); + mpc_clear (y); + + return inex; +} diff --git a/mpc/src/tanh.c b/mpc/src/tanh.c new file mode 100644 index 0000000000..78f21037cf --- /dev/null +++ b/mpc/src/tanh.c @@ -0,0 +1,47 @@ +/* mpc_tanh -- hyperbolic tangent of a complex number. + +Copyright (C) 2008, 2009, 2011, 2012 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_tanh (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd) +{ + /* tanh(op) = -i*tan(i*op) = conj(-i*tan(conj(-i*op))) */ + mpc_t z; + mpc_t tan_z; + int inex; + + /* z := conj(-i * op) and rop = conj(-i * tan(z)), in other words, we have + to switch real and imaginary parts. Let us set them without copying + significands. */ + mpc_realref (z)[0] = mpc_imagref (op)[0]; + mpc_imagref (z)[0] = mpc_realref (op)[0]; + mpc_realref (tan_z)[0] = mpc_imagref (rop)[0]; + mpc_imagref (tan_z)[0] = mpc_realref (rop)[0]; + + inex = mpc_tan (tan_z, z, MPC_RND (MPC_RND_IM (rnd), MPC_RND_RE (rnd))); + + /* tan_z and rop parts share the same significands, copy the rest now. */ + mpc_realref (rop)[0] = mpc_imagref (tan_z)[0]; + mpc_imagref (rop)[0] = mpc_realref (tan_z)[0]; + + /* swap inexact flags for real and imaginary parts */ + return MPC_INEX (MPC_INEX_IM (inex), MPC_INEX_RE (inex)); +} diff --git a/mpc/src/uceil_log2.c b/mpc/src/uceil_log2.c new file mode 100644 index 0000000000..df946128d8 --- /dev/null +++ b/mpc/src/uceil_log2.c @@ -0,0 +1,33 @@ +/* mpc_ceil_log2 - returns ceil(log(d)/log(2)) + +Copyright (C) 2004, 2009, 2010 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +/* returns ceil(log(d)/log(2)) if d > 0 */ +/* Don't use count_leading_zeros since it is in longlong.h */ +mpfr_prec_t +mpc_ceil_log2 (mpfr_prec_t d) +{ + mpfr_prec_t exp; + + for (exp = 0; d > 1; d = (d + 1) / 2) + exp++; + return exp; +} diff --git a/mpc/src/ui_div.c b/mpc/src/ui_div.c new file mode 100644 index 0000000000..6e3b955b53 --- /dev/null +++ b/mpc/src/ui_div.c @@ -0,0 +1,36 @@ +/* mpc_ui_div -- Divide an unsigned long int by a complex number. + +Copyright (C) 2002, 2009 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include <limits.h> +#include "mpc-impl.h" + +int +mpc_ui_div (mpc_ptr a, unsigned long int b, mpc_srcptr c, mpc_rnd_t rnd) +{ + int inex; + mpc_t bb; + + mpc_init2 (bb, sizeof(unsigned long int) * CHAR_BIT); + mpc_set_ui (bb, b, rnd); /* exact */ + inex = mpc_div (a, bb, c, rnd); + mpc_clear (bb); + + return inex; +} diff --git a/mpc/src/ui_ui_sub.c b/mpc/src/ui_ui_sub.c new file mode 100644 index 0000000000..597ee0566c --- /dev/null +++ b/mpc/src/ui_ui_sub.c @@ -0,0 +1,34 @@ +/* mpc_ui_ui_sub -- Subtract a complex number from another one given + implicitly by its real and imaginary parts of type unsigned long int. + +Copyright (C) 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_ui_ui_sub (mpc_ptr rop, unsigned long int re, unsigned long int im, + mpc_srcptr op, mpc_rnd_t rnd) +{ + int inex_re, inex_im; + + inex_re = mpfr_ui_sub (mpc_realref (rop), re, mpc_realref (op), MPC_RND_RE (rnd)); + inex_im = mpfr_ui_sub (mpc_imagref (rop), im, mpc_imagref (op), MPC_RND_IM (rnd)); + + return MPC_INEX (inex_re, inex_im); +} diff --git a/mpc/src/urandom.c b/mpc/src/urandom.c new file mode 100644 index 0000000000..f8e5f9004d --- /dev/null +++ b/mpc/src/urandom.c @@ -0,0 +1,32 @@ +/* mpc_urandom -- Generate a random complex number uniformly distributed in + the interval [0,1[. + +Copyright (C) 2008, 2009, 2011 INRIA + +This file is part of GNU MPC. + +GNU MPC is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see http://www.gnu.org/licenses/ . +*/ + +#include "mpc-impl.h" + +int +mpc_urandom (mpc_ptr a, gmp_randstate_t state) +{ + int r, i; + + r = mpfr_urandomb (mpc_realref(a), state); + i = mpfr_urandomb (mpc_imagref(a), state); + return r && i; +} |