├── .rdoc_options ├── .document ├── test ├── lib │ └── helper.rb └── test_mathn.rb ├── .github ├── dependabot.yml └── workflows │ ├── test.yml │ ├── push_gem.yml │ └── package.yml ├── bin ├── setup └── console ├── .gitignore ├── rakelib └── epoch.rake ├── Gemfile ├── Rakefile ├── lib ├── mathn │ ├── complex.rb │ └── rational.rb └── mathn.rb ├── mathn.gemspec ├── BSDL ├── COPYING ├── README.md └── LEGAL /.rdoc_options: -------------------------------------------------------------------------------- 1 | --- 2 | main_page: README.md 3 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | BSDL 2 | COPYING 3 | LEGAL 4 | README.md 5 | lib/ 6 | -------------------------------------------------------------------------------- /test/lib/helper.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require "core_assertions" 3 | 4 | Test::Unit::TestCase.include Test::Unit::CoreAssertions 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /checksums/ 9 | /spec/reports/ 10 | /tmp/ 11 | *.bundle 12 | *.so 13 | *.dll 14 | -------------------------------------------------------------------------------- /rakelib/epoch.rake: -------------------------------------------------------------------------------- 1 | task "build" => "date_epoch" 2 | 3 | task "date_epoch" do 4 | ENV["SOURCE_DATE_EPOCH"] = IO.popen(%W[git -C #{__dir__} log -1 --no-show-signature --format=%ct], &:read).chomp 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | gem "cmath" 6 | group :development do 7 | gem "bundler" 8 | gem "rake" 9 | gem "test-unit" 10 | gem "test-unit-ruby-core" 11 | gem "irb" 12 | end 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test/lib" 6 | t.ruby_opts << "-rhelper" 7 | t.test_files = FileList['test/**/test_*.rb'] 8 | end 9 | 10 | ENV['RUBYOPT'] = "-w" 11 | 12 | task :default => [:test] 13 | -------------------------------------------------------------------------------- /lib/mathn/complex.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ;# mathn/complex 4 | module Math::N 5 | refine ::Complex do 6 | # Returns the canonicalized real number if the imaginary part is 7 | # zero. Otherwise returns +self+. 8 | def canonicalize 9 | if imag.zero? 10 | real.canonicalize 11 | else 12 | self 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/mathn/rational.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ;# mathn/rational 4 | module Math::N 5 | refine ::Rational do 6 | # Returns the canonicalized numerator if the denominator is one. 7 | # Otherwise returns +self+. 8 | def canonicalize 9 | if denominator == 1 10 | numerator.canonicalize 11 | else 12 | self 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen-string-literal: true 3 | 4 | lock = File.expand_path("../Gemfile.lock", __dir__) 5 | File.unlink(lock) rescue nil 6 | require "bundler/setup" 7 | File.unlink(lock) rescue nil 8 | require "irb" 9 | require "mathn" 10 | 11 | STDOUT.sync = true 12 | IRB.setup(IRB.conf[:AP_NAME] = $0 = "mathn::console") 13 | irb = IRB::Irb.new 14 | irb.context.workspace.evaluate("using Math::N") 15 | irb.run(IRB.conf) 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ruby-versions: 7 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master 8 | with: 9 | engine: cruby 10 | min_version: 2.5 11 | 12 | build: 13 | needs: ruby-versions 14 | name: build (${{ matrix.ruby }} / ${{ matrix.os }}) 15 | strategy: 16 | matrix: 17 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} 18 | os: [ ubuntu-latest, macos-latest, windows-latest ] 19 | exclude: 20 | - { os: macos-latest, ruby: '2.5' } 21 | include: 22 | - { os: macos-15-intel, ruby: '2.5' } 23 | runs-on: ${{ matrix.os }} 24 | steps: 25 | - uses: actions/checkout@v6.0.1 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | - name: Install dependencies 31 | run: bundle install 32 | - name: Run test 33 | run: bundle exec rake 34 | -------------------------------------------------------------------------------- /mathn.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | version = File.read(File.join(__dir__, "lib/mathn.rb"))[/^ *VERSION *= *"\K.*?(?=")/] 3 | 4 | Gem::Specification.new do |spec| 5 | spec.name = "mathn" 6 | spec.version = version 7 | spec.authors = ["Keiju ISHITSUKA"] 8 | spec.email = ["keiju@ishitsuka.com"] 9 | 10 | spec.summary = "Deprecated library that extends math operations." 11 | spec.description = "Deprecated library that extends math operations." 12 | spec.homepage = "https://github.com/ruby/mathn" 13 | spec.license = "BSD-2-Clause" 14 | 15 | gemspec = File.basename(__FILE__) 16 | spec.files = Dir.chdir(__dir__) do 17 | `git ls-files -z`.split("\x0").reject do |f| 18 | f == gemspec or 19 | f.match(%r{\A(?:bin|test|spec|features|rakelib)/|\A(?:Gem|Rake)file|\.(?:git|travis)}) 20 | end 21 | end 22 | spec.bindir = "exe" 23 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 24 | spec.required_ruby_version = ">= 2.5" 25 | spec.require_paths = ["lib"] 26 | 27 | spec.add_dependency "cmath" 28 | end 29 | -------------------------------------------------------------------------------- /.github/workflows/push_gem.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem to rubygems.org 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | push: 13 | if: github.repository_owner == 'ruby' 14 | runs-on: ubuntu-latest 15 | 16 | environment: 17 | name: rubygems.org 18 | url: https://rubygems.org/gems/mathn 19 | 20 | permissions: 21 | contents: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Harden Runner 26 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 27 | with: 28 | egress-policy: audit 29 | 30 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.1 31 | 32 | - name: Set up Ruby 33 | uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 34 | with: 35 | bundler-cache: true 36 | ruby-version: ruby 37 | 38 | - name: Publish to RubyGems 39 | uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2 40 | 41 | - name: Create GitHub release 42 | run: | 43 | tag_name="$(git describe --tags --abbrev=0)" 44 | gh release create "${tag_name}" --verify-tag --generate-notes 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | -------------------------------------------------------------------------------- /BSDL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: package 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ruby-versions: 7 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master 8 | with: 9 | engine: cruby 10 | min_version: 2.5 11 | 12 | build: 13 | needs: ruby-versions 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | ruby: [ '${{ needs.ruby-versions.outputs.latest }}', 'head' ] 18 | os: [ ubuntu-latest, macos-latest, windows-latest ] 19 | exclude: 20 | - { ruby: head, os: windows-latest } 21 | steps: 22 | - name: git config 23 | run: | 24 | git config --global core.autocrlf false 25 | git config --global core.eol lf 26 | git config --global advice.detachedHead 0 27 | - uses: actions/checkout@v6.0.1 28 | - name: Set up Ruby 29 | uses: ruby/setup-ruby@v1 30 | with: 31 | ruby-version: ${{ matrix.ruby }} 32 | - name: Install dependencies 33 | run: bundle install 34 | - name: Package 35 | id: package 36 | run: | 37 | rake build 38 | pkg="${GITHUB_REPOSITORY#*/}-${RUNNING_OS%-*}" 39 | RUBY_VERSION=${{matrix.ruby}} 40 | if [ "$RUBY_VERSION" = head ]; then 41 | pkg=$pkg-$RUBY_VERSION 42 | fi 43 | echo "pkg=$pkg" >> $GITHUB_OUTPUT 44 | env: 45 | RUNNING_OS: ${{matrix.os}} 46 | shell: bash 47 | - name: Upload package 48 | uses: actions/upload-artifact@v6 49 | with: 50 | path: pkg/* 51 | name: ${{steps.package.outputs.pkg}} 52 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Ruby is copyrighted free software by Yukihiro Matsumoto . 2 | You can redistribute it and/or modify it under either the terms of the 3 | 2-clause BSDL (see the file BSDL), or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a. place your modifications in the Public Domain or otherwise 13 | make them Freely Available, such as by posting said 14 | modifications to Usenet or an equivalent medium, or by allowing 15 | the author to include your modifications in the software. 16 | 17 | b. use the modified software only within your corporation or 18 | organization. 19 | 20 | c. give non-standard binaries non-standard names, with 21 | instructions on where to get the original software distribution. 22 | 23 | d. make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or binary form, 26 | provided that you do at least ONE of the following: 27 | 28 | a. distribute the binaries and library files of the software, 29 | together with instructions (in the manual page or equivalent) 30 | on where to get the original distribution. 31 | 32 | b. accompany the distribution with the machine-readable source of 33 | the software. 34 | 35 | c. give non-standard binaries non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d. make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under these terms. 43 | 44 | For the list of those files and their copying conditions, see the 45 | file LEGAL. 46 | 47 | 5. The scripts and library files supplied as input to or produced as 48 | output from the software do not automatically fall under the 49 | copyright of the software, but belong to whomever generated them, 50 | and may be sold commercially, and may be aggregated with this 51 | software. 52 | 53 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 54 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 55 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 56 | PURPOSE. 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mathn 2 | 3 | [![Build Status](https://travis-ci.org/ruby/mathn.svg?branch=master)](https://travis-ci.org/ruby/mathn) 4 | 5 | mathn serves to make mathematical operations more precise in Ruby and to integrate other mathematical standard libraries. 6 | 7 | Without mathn: 8 | 9 | ```ruby 10 | 3 / 2 => 1 # Integer 11 | ``` 12 | 13 | With mathn: 14 | 15 | ```ruby 16 | require "mathn" 17 | using Math::N 18 | 3 / 2 => 3/2 # Rational 19 | ``` 20 | 21 | mathn keeps value in exact terms. 22 | 23 | Without mathn: 24 | 25 | ```ruby 26 | 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 18 27 | ``` 28 | 29 | With mathn: 30 | 31 | ```ruby 32 | require "mathn" 33 | using Math::N 34 | 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 20 35 | ``` 36 | 37 | ## Global Behavioral Changes 38 | 39 | While older version of 'mathn', just by required, caused changes to 40 | the behavior (and even the types) of operations on classes like 41 | `Integer`, newer `mathn` introduces the refinements `Math::N`, and you 42 | have to enable it. 43 | 44 | Before ruby 2.5, `mathn` was part of the ruby standard library. It was 45 | [deprecated in ruby 2.2.0], and [removed from ruby 2.5.0]. In order to 46 | use the library with a current version of ruby, you must install it as a 47 | gem. 48 | 49 | [deprecated in ruby 2.2.0]: https://github.com/ruby/ruby/blob/v2_2_0/NEWS#stdlib-compatibility-issues-excluding-feature-bug-fixes 50 | [removed from ruby 2.5.0]: https://github.com/ruby/ruby/blob/ruby_2_5/NEWS#stdlib-compatibility-issues-excluding-feature-bug-fixes 51 | 52 | ## Installation 53 | 54 | Add this line to your application's Gemfile: 55 | 56 | ```ruby 57 | gem 'mathn' 58 | ``` 59 | 60 | And then execute: 61 | 62 | ```console 63 | $ bundle 64 | ``` 65 | 66 | Or install it yourself as: 67 | 68 | ```console 69 | $ gem install mathn 70 | ``` 71 | 72 | ## Usage 73 | 74 | ```ruby 75 | require 'mathn' 76 | using Math::N 77 | ``` 78 | 79 | ## Development 80 | 81 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 82 | 83 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 84 | 85 | ## Contributing 86 | 87 | Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/mathn. 88 | 89 | 90 | ## License 91 | 92 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 93 | -------------------------------------------------------------------------------- /LEGAL: -------------------------------------------------------------------------------- 1 | # -*- rdoc -*- 2 | 3 | = LEGAL NOTICE INFORMATION 4 | -------------------------- 5 | 6 | All the files in this distribution are covered under either the Ruby's 7 | license (see the file COPYING) or public-domain except some files 8 | mentioned below. 9 | 10 | == MIT License 11 | >>> 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | == Old-style BSD license 32 | >>> 33 | Redistribution and use in source and binary forms, with or without 34 | modification, are permitted provided that the following conditions 35 | are met: 36 | 1. Redistributions of source code must retain the above copyright 37 | notice, this list of conditions and the following disclaimer. 38 | 2. Redistributions in binary form must reproduce the above copyright 39 | notice, this list of conditions and the following disclaimer in the 40 | documentation and/or other materials provided with the distribution. 41 | 3. Neither the name of the University nor the names of its contributors 42 | may be used to endorse or promote products derived from this software 43 | without specific prior written permission. 44 | 45 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 | SUCH DAMAGE. 56 | 57 | IMPORTANT NOTE:: 58 | 59 | From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change 60 | paragraph 3 above is now null and void. 61 | -------------------------------------------------------------------------------- /lib/mathn.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The built-in module for mathematical functions 4 | module Math end if false # for RDoc 5 | 6 | require "cmath" 7 | require_relative "mathn/complex" 8 | require_relative "mathn/rational" 9 | 10 | ## 11 | # = mathn 12 | # 13 | # mathn serves to make mathematical operations more precise in Ruby 14 | # and to integrate other mathematical standard libraries. 15 | # 16 | # Without mathn: 17 | # 18 | # using Math::N 19 | # 3 / 2 => 1 # Integer 20 | # 21 | # With mathn: 22 | # 23 | # using Math::N 24 | # 3 / 2 => 3/2 # Rational 25 | # 26 | # mathn keeps value in exact terms. 27 | # 28 | # Without mathn: 29 | # 30 | # 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 18 31 | # 32 | # With mathn: 33 | # 34 | # using Math::N 35 | # 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 20 36 | # 37 | # 38 | # When you require 'mathn', the libraries for CMath is also loaded. 39 | # 40 | # == Copyright 41 | # 42 | # Author: Keiju ISHITSUKA (SHL Japan Inc.) 43 | 44 | module Math::N 45 | # The version string 46 | VERSION = "0.2.0" 47 | 48 | refine ::Numeric do 49 | ## 50 | # Returns the canonicalized result. 51 | def canonicalize; itself; end if false # for RDoc 52 | alias canonicalize itself 53 | end 54 | 55 | using self # for canonicalize methods 56 | 57 | # :stopdoc: 58 | def +(other) super.canonicalize end 59 | def -(other) super.canonicalize end 60 | def *(other) super.canonicalize end 61 | def /(other) super.canonicalize end 62 | def quo(other) super.canonicalize end 63 | def **(other) super.canonicalize end 64 | # :startdoc: 65 | 66 | # Transplant per methods. 67 | canon = public_instance_methods(false).map do |n, h| 68 | [n, instance_method(n)] 69 | end 70 | for klass in [::Integer, ::Rational, ::Complex] 71 | refine klass do 72 | canon.each {|n, m| define_method(n, m)} 73 | end 74 | end 75 | 76 | # Integer's division is enhanced to return more precise values from 77 | # mathematical expressions. 78 | class ::Integer 79 | ## 80 | # +/+ defines the Rational division for Integer. 81 | # 82 | # require 'mathn' 83 | # 2/3*3 # => 0 84 | # (2**72) / ((2**70) * 3) # => 1 85 | # 86 | # using Math::N 87 | # 2/3*3 # => 2 88 | # (2**72) / ((2**70) * 3) # => 4/3 89 | def quo(other) super.canonicalize end 90 | alias / quo 91 | end if false # for RDoc 92 | 93 | refine ::Integer do 94 | alias / quo 95 | end 96 | 97 | math = refine ::Math do 98 | module_function 99 | 100 | ## 101 | # Computes the square root of +a+. It makes use of +Complex+ and 102 | # +Rational+ to have no rounding errors if possible. 103 | # 104 | # Standard Math module behaviour: 105 | # Math.sqrt(4/9) # => 0.0 106 | # Math.sqrt(4.0/9.0) # => 0.6666666666666666 107 | # Math.sqrt(-4/9) # => Errno::EDOM: Numerical argument out of domain - sqrt 108 | # 109 | # When using +Math::N+, this is changed to: 110 | # 111 | # require 'mathn' 112 | # using Math::N 113 | # Math.sqrt(4/9) # => 2/3 114 | # Math.sqrt(4.0/9.0) # => 0.666666666666666 115 | # Math.sqrt(-4/9) # => Complex(0, 2/3) 116 | 117 | def sqrt(a) 118 | return super unless a.respond_to?(:negative?) 119 | return a if a.respond_to?(:nan?) and a.nan? 120 | negative = a.negative? 121 | 122 | # Compute square root of a non negative number. 123 | case a 124 | when Float 125 | a = super(a.abs) 126 | when Rational 127 | a = sqrt(a.numerator.abs).quo sqrt(a.denominator.abs) 128 | else 129 | rt = Integer.sqrt(a = a.abs) 130 | a = rt * rt == a ? rt : super(a) 131 | end 132 | negative ? Complex(0, a) : a 133 | end 134 | 135 | ## 136 | # Computes the cubic root of +a+. It makes use of +Complex+ and 137 | # +Rational+ to have no rounding errors if possible. 138 | # 139 | # Standard Math module behaviour: 140 | # Math.cbrt(8/27) # => 0.0 141 | # Math.cbrt(8.0/27.0) # => 0.666666666666666 142 | # Math.cbrt(-8/27) # => -1.0 143 | # 144 | # When using +Math::N+, this is changed to: 145 | # 146 | # require 'mathn' 147 | # using Math::N 148 | # Math.cbrt(8/27) # => (2/3) 149 | # Math.cbrt(8.0/27.0) # => 0.666666666666666 150 | # Math.cbrt(-8/27) # => (-2/3) 151 | 152 | def cbrt(a) 153 | case a 154 | when Integer 155 | rt = super 156 | rt ** 3 == a ? Integer(rt) : rt 157 | when Rational 158 | cbrt(a.numerator).quo cbrt(a.denominator) 159 | when Complex 160 | a ** (1/3r) 161 | else 162 | super 163 | end 164 | end 165 | end 166 | 167 | # Transplant module functions. 168 | refine ::Math.singleton_class do 169 | math.private_instance_methods(false).each do |m| 170 | next unless math.respond_to?(m) 171 | public define_method(m, Math.instance_method(m)) 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /test/test_mathn.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | require 'test/unit' 3 | require 'mathn' 4 | 5 | module TestMathn 6 | HALF = 1/2r 7 | 8 | class WithoutMathn < Test::Unit::TestCase 9 | def test_plus 10 | assert_kind_of(Rational, (1/2r)+(1/2r)) 11 | assert_kind_of(Complex, (1-1i)+1i) 12 | end 13 | 14 | def test_minus 15 | assert_kind_of(Rational, (1/2r)-(1/2r)) 16 | assert_kind_of(Complex, (1+1i)-1i) 17 | end 18 | 19 | def test_mul 20 | assert_kind_of(Rational, 1/2r*2) 21 | assert_equal(0, 1/2*2) 22 | 23 | x = (1+1i)*(1-1i) 24 | assert_kind_of(Complex, x) 25 | assert_equal(2, x) 26 | end 27 | 28 | def test_div 29 | assert_kind_of(Rational, (4/3r)/(2/3r)) 30 | assert_equal(0, (2/3)/(4/3)) 31 | 32 | x = (2+2i)/(1+1i) 33 | assert_kind_of(Complex, x) 34 | assert_equal(2, x) 35 | end 36 | end 37 | 38 | class WithMathn < Test::Unit::TestCase 39 | using Math::N 40 | 41 | def test_plus 42 | assert_kind_of(Integer, (1/2r)+(1/2r)) 43 | assert_kind_of(Integer, (1-1i)+1i) 44 | end 45 | 46 | def test_minus 47 | assert_kind_of(Integer, (1/2r)-(1/2r)) 48 | assert_kind_of(Integer, (1+1i)-1i) 49 | end 50 | 51 | def test_mul 52 | assert_kind_of(Integer, 1/2r*2) 53 | assert_equal(1, 1/2*2) 54 | 55 | x = (1+1i)*(1-1i) 56 | assert_kind_of(Integer, x) 57 | assert_equal(2, x) 58 | end 59 | 60 | def test_div 61 | assert_kind_of(Integer, (4/3r)/(2/3r)) 62 | assert_equal(1/2r, (2/3)/(4/3)) 63 | 64 | x = (2+2i)/(1+1i) 65 | assert_kind_of(Integer, x) 66 | assert_equal(2, x) 67 | end 68 | 69 | def test_power 70 | assert_equal(1, 1**2) 71 | assert_kind_of(Integer, (1 << 126)**2) 72 | assert_equal(1, Complex(0,1)**4) 73 | assert_equal(1i, Complex(0,1)**5) 74 | end 75 | 76 | def test_quo 77 | assert_equal(1/2r, 1.quo(2) , '[ruby-core:41575]') 78 | end 79 | 80 | def test_floor 81 | assert_equal( 2, ( 13/5).floor) 82 | assert_equal( 2, ( 5/2).floor) 83 | assert_equal( 2, ( 12/5).floor) 84 | assert_equal(-3, (-12/5).floor) 85 | assert_equal(-3, ( -5/2).floor) 86 | assert_equal(-3, (-13/5).floor) 87 | 88 | assert_equal( 2, ( 13/5).floor(0)) 89 | assert_equal( 2, ( 5/2).floor(0)) 90 | assert_equal( 2, ( 12/5).floor(0)) 91 | assert_equal(-3, (-12/5).floor(0)) 92 | assert_equal(-3, ( -5/2).floor(0)) 93 | assert_equal(-3, (-13/5).floor(0)) 94 | 95 | assert_equal(( 13/5), ( 13/5).floor(2)) 96 | assert_equal(( 5/2), ( 5/2).floor(2)) 97 | assert_equal(( 12/5), ( 12/5).floor(2)) 98 | assert_equal((-12/5), (-12/5).floor(2)) 99 | assert_equal(( -5/2), ( -5/2).floor(2)) 100 | assert_equal((-13/5), (-13/5).floor(2)) 101 | end 102 | 103 | def test_ceil 104 | assert_equal( 3, ( 13/5).ceil) 105 | assert_equal( 3, ( 5/2).ceil) 106 | assert_equal( 3, ( 12/5).ceil) 107 | assert_equal(-2, (-12/5).ceil) 108 | assert_equal(-2, ( -5/2).ceil) 109 | assert_equal(-2, (-13/5).ceil) 110 | 111 | assert_equal( 3, ( 13/5).ceil(0)) 112 | assert_equal( 3, ( 5/2).ceil(0)) 113 | assert_equal( 3, ( 12/5).ceil(0)) 114 | assert_equal(-2, (-12/5).ceil(0)) 115 | assert_equal(-2, ( -5/2).ceil(0)) 116 | assert_equal(-2, (-13/5).ceil(0)) 117 | 118 | assert_equal(( 13/5), ( 13/5).ceil(2)) 119 | assert_equal(( 5/2), ( 5/2).ceil(2)) 120 | assert_equal(( 12/5), ( 12/5).ceil(2)) 121 | assert_equal((-12/5), (-12/5).ceil(2)) 122 | assert_equal(( -5/2), ( -5/2).ceil(2)) 123 | assert_equal((-13/5), (-13/5).ceil(2)) 124 | end 125 | 126 | def test_truncate 127 | assert_equal( 2, ( 13/5).truncate) 128 | assert_equal( 2, ( 5/2).truncate) 129 | assert_equal( 2, ( 12/5).truncate) 130 | assert_equal(-2, (-12/5).truncate) 131 | assert_equal(-2, ( -5/2).truncate) 132 | assert_equal(-2, (-13/5).truncate) 133 | 134 | assert_equal( 2, ( 13/5).truncate(0)) 135 | assert_equal( 2, ( 5/2).truncate(0)) 136 | assert_equal( 2, ( 12/5).truncate(0)) 137 | assert_equal(-2, (-12/5).truncate(0)) 138 | assert_equal(-2, ( -5/2).truncate(0)) 139 | assert_equal(-2, (-13/5).truncate(0)) 140 | 141 | assert_equal(( 13/5), ( 13/5).truncate(2)) 142 | assert_equal(( 5/2), ( 5/2).truncate(2)) 143 | assert_equal(( 12/5), ( 12/5).truncate(2)) 144 | assert_equal((-12/5), (-12/5).truncate(2)) 145 | assert_equal(( -5/2), ( -5/2).truncate(2)) 146 | assert_equal((-13/5), (-13/5).truncate(2)) 147 | end 148 | 149 | def test_round 150 | assert_equal( 3, ( 13/5).round) 151 | assert_equal( 3, ( 5/2).round) 152 | assert_equal( 2, ( 12/5).round) 153 | assert_equal(-2, (-12/5).round) 154 | assert_equal(-3, ( -5/2).round) 155 | assert_equal(-3, (-13/5).round) 156 | 157 | assert_equal( 3, ( 13/5).round(0)) 158 | assert_equal( 3, ( 5/2).round(0)) 159 | assert_equal( 2, ( 12/5).round(0)) 160 | assert_equal(-2, (-12/5).round(0)) 161 | assert_equal(-3, ( -5/2).round(0)) 162 | assert_equal(-3, (-13/5).round(0)) 163 | 164 | assert_equal(( 13/5), ( 13/5).round(2)) 165 | assert_equal(( 5/2), ( 5/2).round(2)) 166 | assert_equal(( 12/5), ( 12/5).round(2)) 167 | assert_equal((-12/5), (-12/5).round(2)) 168 | assert_equal(( -5/2), ( -5/2).round(2)) 169 | assert_equal((-13/5), (-13/5).round(2)) 170 | 171 | assert_equal( 3, ( 13/5).round(half: :even)) 172 | assert_equal( 2, ( 5/2).round(half: :even)) 173 | assert_equal( 2, ( 12/5).round(half: :even)) 174 | assert_equal(-2, (-12/5).round(half: :even)) 175 | assert_equal(-2, ( -5/2).round(half: :even)) 176 | assert_equal(-3, (-13/5).round(half: :even)) 177 | 178 | assert_equal( 3, ( 13/5).round(0, half: :even)) 179 | assert_equal( 2, ( 5/2).round(0, half: :even)) 180 | assert_equal( 2, ( 12/5).round(0, half: :even)) 181 | assert_equal(-2, (-12/5).round(0, half: :even)) 182 | assert_equal(-2, ( -5/2).round(0, half: :even)) 183 | assert_equal(-3, (-13/5).round(0, half: :even)) 184 | 185 | assert_equal(( 13/5), ( 13/5).round(2, half: :even)) 186 | assert_equal(( 5/2), ( 5/2).round(2, half: :even)) 187 | assert_equal(( 12/5), ( 12/5).round(2, half: :even)) 188 | assert_equal((-12/5), (-12/5).round(2, half: :even)) 189 | assert_equal(( -5/2), ( -5/2).round(2, half: :even)) 190 | assert_equal((-13/5), (-13/5).round(2, half: :even)) 191 | 192 | assert_equal( 3, ( 13/5).round(half: :up)) 193 | assert_equal( 3, ( 5/2).round(half: :up)) 194 | assert_equal( 2, ( 12/5).round(half: :up)) 195 | assert_equal(-2, (-12/5).round(half: :up)) 196 | assert_equal(-3, ( -5/2).round(half: :up)) 197 | assert_equal(-3, (-13/5).round(half: :up)) 198 | 199 | assert_equal( 3, ( 13/5).round(0, half: :up)) 200 | assert_equal( 3, ( 5/2).round(0, half: :up)) 201 | assert_equal( 2, ( 12/5).round(0, half: :up)) 202 | assert_equal(-2, (-12/5).round(0, half: :up)) 203 | assert_equal(-3, ( -5/2).round(0, half: :up)) 204 | assert_equal(-3, (-13/5).round(0, half: :up)) 205 | 206 | assert_equal(( 13/5), ( 13/5).round(2, half: :up)) 207 | assert_equal(( 5/2), ( 5/2).round(2, half: :up)) 208 | assert_equal(( 12/5), ( 12/5).round(2, half: :up)) 209 | assert_equal((-12/5), (-12/5).round(2, half: :up)) 210 | assert_equal(( -5/2), ( -5/2).round(2, half: :up)) 211 | assert_equal((-13/5), (-13/5).round(2, half: :up)) 212 | 213 | assert_equal( 3, ( 13/5).round(half: :down)) 214 | assert_equal( 2, ( 5/2).round(half: :down)) 215 | assert_equal( 2, ( 12/5).round(half: :down)) 216 | assert_equal(-2, (-12/5).round(half: :down)) 217 | assert_equal(-2, ( -5/2).round(half: :down)) 218 | assert_equal(-3, (-13/5).round(half: :down)) 219 | 220 | assert_equal( 3, ( 13/5).round(0, half: :down)) 221 | assert_equal( 2, ( 5/2).round(0, half: :down)) 222 | assert_equal( 2, ( 12/5).round(0, half: :down)) 223 | assert_equal(-2, (-12/5).round(0, half: :down)) 224 | assert_equal(-2, ( -5/2).round(0, half: :down)) 225 | assert_equal(-3, (-13/5).round(0, half: :down)) 226 | 227 | assert_equal(( 13/5), ( 13/5).round(2, half: :down)) 228 | assert_equal(( 5/2), ( 5/2).round(2, half: :down)) 229 | assert_equal(( 12/5), ( 12/5).round(2, half: :down)) 230 | assert_equal((-12/5), (-12/5).round(2, half: :down)) 231 | assert_equal(( -5/2), ( -5/2).round(2, half: :down)) 232 | assert_equal((-13/5), (-13/5).round(2, half: :down)) 233 | end 234 | 235 | def test_rational 236 | assert_equal(-5, "-5".to_r) 237 | assert_equal(1, "5/5".to_r) 238 | assert_equal(5, "5e0".to_r) 239 | end 240 | end 241 | end 242 | --------------------------------------------------------------------------------