Compare commits
No commits in common. "main" and "1dbed201e631d39216e5bbc2ad818558add89575" have entirely different histories.
main
...
1dbed201e6
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
||||||
# Generated by Nix
|
# Generated by Nix
|
||||||
/.pre-commit-config.yaml
|
/.pre-commit-config.yaml
|
||||||
/result
|
|
||||||
|
|
||||||
# Meson
|
# Meson
|
||||||
/build
|
/build
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
labels:
|
|
||||||
type: exec
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: flake check
|
|
||||||
image: bash
|
|
||||||
commands:
|
|
||||||
- nix flake check
|
|
||||||
|
|
||||||
- name: package check
|
|
||||||
image: bash
|
|
||||||
commands:
|
|
||||||
- nix build
|
|
||||||
|
|
||||||
- name: notify
|
|
||||||
image: bash
|
|
||||||
secrets:
|
|
||||||
- source: matrix_roomid
|
|
||||||
target: room
|
|
||||||
- source: matrix_username
|
|
||||||
target: user
|
|
||||||
- source: matrix_password
|
|
||||||
target: pass
|
|
||||||
- source: matrix_homeserver
|
|
||||||
target: address
|
|
||||||
commands:
|
|
||||||
- nix run github:ambroisie/matrix-notifier
|
|
||||||
when:
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
- success
|
|
|
@ -1,18 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(abacus VERSION 0.0.0 LANGUAGES CXX)
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
add_library(common_options INTERFACE)
|
|
||||||
target_compile_features(common_options INTERFACE
|
|
||||||
cxx_std_20
|
|
||||||
)
|
|
||||||
target_compile_options(common_options INTERFACE
|
|
||||||
-Wall
|
|
||||||
-Wextra
|
|
||||||
)
|
|
||||||
target_include_directories(common_options INTERFACE
|
|
||||||
src
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(src)
|
|
||||||
add_subdirectory(tests)
|
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) [year] [fullname]
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
74
flake.nix
74
flake.nix
|
@ -29,40 +29,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, futils, nixpkgs, pre-commit-hooks }:
|
outputs = { self, futils, nixpkgs, pre-commit-hooks }:
|
||||||
{
|
futils.lib.eachDefaultSystem (system:
|
||||||
overlays.default = final: prev: {
|
|
||||||
abacus = final.stdenv.mkDerivation {
|
|
||||||
pname = "abacus";
|
|
||||||
version = "0.0.0";
|
|
||||||
|
|
||||||
src = self;
|
|
||||||
|
|
||||||
nativeBuildInputs = with final; [
|
|
||||||
bison
|
|
||||||
cmake
|
|
||||||
flex
|
|
||||||
ninja
|
|
||||||
pkg-config
|
|
||||||
];
|
|
||||||
|
|
||||||
checkInputs = with final; [
|
|
||||||
gtest
|
|
||||||
];
|
|
||||||
|
|
||||||
doCheck = true;
|
|
||||||
|
|
||||||
meta = with final.lib; {
|
|
||||||
description = "A simple calculator using big numbers";
|
|
||||||
homepage = "https://gitea.belanyi.fr/ambroisie/abacus";
|
|
||||||
license = licenses.mit;
|
|
||||||
maintainers = [ ambroisie ];
|
|
||||||
platforms = platforms.unix;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} // futils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; overlays = [ self.overlays.default ]; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
checks = {
|
checks = {
|
||||||
|
@ -87,17 +56,44 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
defaultPackage = self.packages.${system}.abacus;
|
||||||
inputsFrom = with self.packages.${system}; [
|
|
||||||
abacus
|
devShell = pkgs.mkShell {
|
||||||
];
|
inherit (self.defaultPackage.${system})
|
||||||
|
buildInputs
|
||||||
|
nativeBuildInputs
|
||||||
|
;
|
||||||
|
|
||||||
inherit (self.checks.${system}.pre-commit) shellHook;
|
inherit (self.checks.${system}.pre-commit) shellHook;
|
||||||
};
|
};
|
||||||
|
|
||||||
packages = futils.lib.flattenTree {
|
packages = futils.lib.flattenTree {
|
||||||
inherit (pkgs) abacus;
|
abacus = pkgs.stdenv.mkDerivation {
|
||||||
default = pkgs.abacus;
|
pname = "abacus";
|
||||||
|
version = "0.0.0";
|
||||||
|
|
||||||
|
src = self;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
bison
|
||||||
|
flex
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
gtest
|
||||||
|
];
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "A simple calculator using big numbers";
|
||||||
|
homepage = "https://gitea.belanyi.fr/ambroisie/abacus";
|
||||||
|
license = licenses.mit;
|
||||||
|
maintainers = [ ambroisie ];
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
13
meson.build
Normal file
13
meson.build
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
project(
|
||||||
|
'abacus',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
license: 'MIT',
|
||||||
|
default_options: [
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('src')
|
||||||
|
subdir('tests')
|
|
@ -1,12 +0,0 @@
|
||||||
add_executable(abacus abacus.cc)
|
|
||||||
target_link_libraries(abacus PRIVATE common_options)
|
|
||||||
|
|
||||||
add_subdirectory(bignum)
|
|
||||||
add_subdirectory(parse)
|
|
||||||
|
|
||||||
target_link_libraries(abacus PRIVATE
|
|
||||||
bignum
|
|
||||||
parse
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS abacus)
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "parse/parser-driver.hh"
|
#include "parser-driver.hh" // FIXME: I would like `parse/parser-driver.hh` path instead...
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
abacus::parse::ParserDriver driver{};
|
abacus::parse::ParserDriver driver{};
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
add_library(bignum STATIC
|
|
||||||
bignum.cc
|
|
||||||
bignum.hh
|
|
||||||
)
|
|
||||||
target_link_libraries(bignum PRIVATE common_options)
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -25,46 +24,12 @@ bool do_less_than(digits_type const& lhs, digits_type const& rhs) {
|
||||||
rhs.rend());
|
rhs.rend());
|
||||||
}
|
}
|
||||||
|
|
||||||
void trim_leading_zeros(digits_type& num) {
|
void do_trim_leading_zeros(digits_type& num) {
|
||||||
auto const it
|
auto const it
|
||||||
= std::find_if(num.rbegin(), num.rend(), [](auto v) { return v != 0; });
|
= std::find_if(num.rbegin(), num.rend(), [](auto v) { return v != 0; });
|
||||||
num.erase(it.base(), num.end());
|
num.erase(it.base(), num.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& do_dump(digits_type const& num, std::ostream& out) {
|
|
||||||
std::copy(num.rbegin(), num.rend(), std::ostream_iterator<int>(out));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// More optimised than full-on div_mod
|
|
||||||
digits_type do_halve(digits_type num) {
|
|
||||||
assert(num.size() != 0);
|
|
||||||
|
|
||||||
int carry = 0;
|
|
||||||
for (auto i = num.rbegin(); i != num.rend(); ++i) {
|
|
||||||
auto const was_odd = (*i % 2) == 1;
|
|
||||||
*i /= 2;
|
|
||||||
*i += carry;
|
|
||||||
if (was_odd) {
|
|
||||||
carry = BASE / 2;
|
|
||||||
} else {
|
|
||||||
carry = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trim_leading_zeros(num);
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_odd(digits_type const& num) {
|
|
||||||
if (num.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (num.front() % 2) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
digits_type do_addition(digits_type const& lhs, digits_type const& rhs) {
|
digits_type do_addition(digits_type const& lhs, digits_type const& rhs) {
|
||||||
int carry = 0;
|
int carry = 0;
|
||||||
digits_type res;
|
digits_type res;
|
||||||
|
@ -83,17 +48,18 @@ digits_type do_addition(digits_type const& lhs, digits_type const& rhs) {
|
||||||
++it2;
|
++it2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto leftover = [=]() {
|
auto it = it1;
|
||||||
if (it1 != end1) {
|
auto end = end1;
|
||||||
return std::span(it1, end1);
|
if (it1 == end1) {
|
||||||
}
|
it = it2;
|
||||||
return std::span(it2, end2);
|
end = end2;
|
||||||
}();
|
}
|
||||||
|
|
||||||
for (auto value : leftover) {
|
while (it != end) {
|
||||||
int addition = value + carry;
|
int addition = *it + carry;
|
||||||
carry = addition / BASE;
|
carry = addition / BASE;
|
||||||
res.push_back(addition % BASE);
|
res.push_back(addition % BASE);
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (carry != 0) {
|
if (carry != 0) {
|
||||||
|
@ -107,7 +73,7 @@ digits_type do_substraction(digits_type const& lhs, digits_type const& rhs) {
|
||||||
assert(!do_less_than(lhs, rhs));
|
assert(!do_less_than(lhs, rhs));
|
||||||
|
|
||||||
digits_type complement;
|
digits_type complement;
|
||||||
auto const take_complement = [](auto num) { return BASE - 1 - num; };
|
auto const take_complement = [](auto num) { return 9 - num; };
|
||||||
std::transform(lhs.begin(), lhs.end(), std::back_inserter(complement),
|
std::transform(lhs.begin(), lhs.end(), std::back_inserter(complement),
|
||||||
take_complement);
|
take_complement);
|
||||||
|
|
||||||
|
@ -116,7 +82,7 @@ digits_type do_substraction(digits_type const& lhs, digits_type const& rhs) {
|
||||||
std::transform(complement.begin(), complement.end(), complement.begin(),
|
std::transform(complement.begin(), complement.end(), complement.begin(),
|
||||||
take_complement);
|
take_complement);
|
||||||
|
|
||||||
trim_leading_zeros(complement);
|
do_trim_leading_zeros(complement);
|
||||||
|
|
||||||
return complement;
|
return complement;
|
||||||
}
|
}
|
||||||
|
@ -144,33 +110,59 @@ std::pair<digits_type, digits_type> do_div_mod(digits_type const& lhs,
|
||||||
throw std::invalid_argument("attempt to divide by zero");
|
throw std::invalid_argument("attempt to divide by zero");
|
||||||
}
|
}
|
||||||
|
|
||||||
digits_type multiple = rhs;
|
|
||||||
digits_type rank;
|
|
||||||
rank.push_back(1);
|
|
||||||
|
|
||||||
while (!do_less_than(lhs, multiple)) {
|
|
||||||
multiple = do_addition(multiple, multiple);
|
|
||||||
rank = do_addition(rank, rank);
|
|
||||||
}
|
|
||||||
|
|
||||||
digits_type quotient;
|
digits_type quotient;
|
||||||
digits_type remainder = lhs;
|
digits_type remainder = lhs;
|
||||||
|
|
||||||
while (!do_less_than(remainder, rhs)) {
|
while (!do_less_than(remainder, rhs)) {
|
||||||
while (do_less_than(remainder, multiple)) {
|
// TODO: use `do_halve` to back down after calculate highest multiple
|
||||||
multiple = do_halve(multiple);
|
digits_type multiple = rhs;
|
||||||
rank = do_halve(rank);
|
digits_type rank;
|
||||||
|
rank.push_back(1);
|
||||||
|
|
||||||
|
digits_type prev_multiple = multiple;
|
||||||
|
digits_type prev_rank = rank;
|
||||||
|
|
||||||
|
while (!do_less_than(remainder, multiple)) {
|
||||||
|
prev_multiple = multiple;
|
||||||
|
prev_rank = rank;
|
||||||
|
multiple = do_addition(multiple, multiple);
|
||||||
|
rank = do_addition(rank, rank);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!do_less_than(multiple, rhs));
|
quotient = do_addition(quotient, prev_rank);
|
||||||
|
remainder = do_substraction(remainder, prev_multiple);
|
||||||
quotient = do_addition(quotient, rank);
|
|
||||||
remainder = do_substraction(remainder, multiple);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair(quotient, remainder);
|
return std::make_pair(quotient, remainder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// More optimised than full-on div_mod
|
||||||
|
digits_type do_halve(digits_type num) {
|
||||||
|
assert(num.size() != 0);
|
||||||
|
|
||||||
|
int carry = 0;
|
||||||
|
for (auto i = num.rbegin(); i != num.rend(); ++i) {
|
||||||
|
auto const was_odd = (*i % 2) == 1;
|
||||||
|
*i /= 2;
|
||||||
|
*i += carry;
|
||||||
|
if (was_odd) {
|
||||||
|
carry = BASE / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_trim_leading_zeros(num);
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_odd(digits_type const& num) {
|
||||||
|
if (num.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (num.front() % 2) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
digits_type do_pow(digits_type lhs, digits_type rhs) {
|
digits_type do_pow(digits_type lhs, digits_type rhs) {
|
||||||
assert(rhs.size() != 0);
|
assert(rhs.size() != 0);
|
||||||
|
|
||||||
|
@ -234,8 +226,10 @@ std::ostream& BigNum::dump(std::ostream& out) const {
|
||||||
if (is_negative()) {
|
if (is_negative()) {
|
||||||
out << '-';
|
out << '-';
|
||||||
}
|
}
|
||||||
|
std::copy(digits_.rbegin(), digits_.rend(),
|
||||||
|
std::ostream_iterator<int>(out));
|
||||||
|
|
||||||
return do_dump(digits_, out);
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::istream& BigNum::read(std::istream& in) {
|
std::istream& BigNum::read(std::istream& in) {
|
||||||
|
@ -363,15 +357,11 @@ bool BigNum::less_than(BigNum const& rhs) const {
|
||||||
return sign_ < rhs.sign_;
|
return sign_ < rhs.sign_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_positive()) {
|
return do_less_than(digits_, rhs.digits_);
|
||||||
return do_less_than(digits_, rhs.digits_);
|
|
||||||
} else {
|
|
||||||
return do_less_than(rhs.digits_, digits_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BigNum::canonicalize() {
|
void BigNum::canonicalize() {
|
||||||
trim_leading_zeros(digits_);
|
do_trim_leading_zeros(digits_);
|
||||||
|
|
||||||
if (digits_.size() == 0) {
|
if (digits_.size() == 0) {
|
||||||
sign_ = 0;
|
sign_ = 0;
|
||||||
|
@ -385,15 +375,15 @@ bool BigNum::is_canonicalized() const {
|
||||||
return sign_ == 0;
|
return sign_ == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `back` is valid since there is at least one element
|
auto const leading_zeros = std::find_if(digits_.rbegin(), digits_.rend(),
|
||||||
auto const has_leading_zero = digits_.back() == 0;
|
[](auto v) { return v != 0; });
|
||||||
if (has_leading_zero) {
|
if (leading_zeros != digits_.rbegin()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const has_overflow = std::any_of(digits_.begin(), digits_.end(),
|
auto const overflow = std::find_if(digits_.begin(), digits_.end(),
|
||||||
[](auto v) { return v >= BASE; });
|
[](auto v) { return v >= BASE; });
|
||||||
if (has_overflow) {
|
if (overflow != digits_.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,8 +413,8 @@ std::pair<BigNum, BigNum> div_mod(BigNum const& lhs, BigNum const& rhs) {
|
||||||
return std::make_pair(BigNum(), BigNum());
|
return std::make_pair(BigNum(), BigNum());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto quotient = BigNum(0);
|
BigNum quotient;
|
||||||
auto remainder = BigNum(0);
|
BigNum remainder;
|
||||||
|
|
||||||
std::tie(quotient.digits_, remainder.digits_)
|
std::tie(quotient.digits_, remainder.digits_)
|
||||||
= do_div_mod(lhs.digits_, rhs.digits_);
|
= do_div_mod(lhs.digits_, rhs.digits_);
|
||||||
|
@ -449,7 +439,7 @@ BigNum pow(BigNum const& lhs, BigNum const& rhs) {
|
||||||
return BigNum();
|
return BigNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = BigNum(0);
|
BigNum res;
|
||||||
res.digits_ = do_pow(lhs.digits_, rhs.digits_);
|
res.digits_ = do_pow(lhs.digits_, rhs.digits_);
|
||||||
|
|
||||||
res.sign_ = is_odd(rhs.digits_) ? lhs.sign_ : 1;
|
res.sign_ = is_odd(rhs.digits_) ? lhs.sign_ : 1;
|
||||||
|
@ -468,7 +458,7 @@ BigNum sqrt(BigNum const& num) {
|
||||||
"attempt to take the square root of a negative number");
|
"attempt to take the square root of a negative number");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = BigNum(0);
|
BigNum res;
|
||||||
|
|
||||||
res.digits_ = do_sqrt(num.digits_);
|
res.digits_ = do_sqrt(num.digits_);
|
||||||
res.sign_ = 1;
|
res.sign_ = 1;
|
||||||
|
@ -478,46 +468,4 @@ BigNum sqrt(BigNum const& num) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigNum log2(BigNum const& num) {
|
|
||||||
assert(num.is_canonicalized());
|
|
||||||
|
|
||||||
if (num.is_zero()) {
|
|
||||||
throw std::invalid_argument("attempt to take the log2 of zero");
|
|
||||||
} else if (num.is_negative()) {
|
|
||||||
throw std::invalid_argument(
|
|
||||||
"attempt to take the log2 of a negative number");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tmp = num;
|
|
||||||
auto res = BigNum(0);
|
|
||||||
auto one = BigNum(1);
|
|
||||||
|
|
||||||
while (tmp > one) {
|
|
||||||
tmp.digits_ = do_halve(tmp.digits_);
|
|
||||||
res += one;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(res.is_canonicalized());
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum log10(BigNum const& num) {
|
|
||||||
assert(num.is_canonicalized());
|
|
||||||
assert(BASE == 10);
|
|
||||||
|
|
||||||
if (num.is_zero()) {
|
|
||||||
throw std::invalid_argument("attempt to take the log10 of zero");
|
|
||||||
} else if (num.is_negative()) {
|
|
||||||
throw std::invalid_argument(
|
|
||||||
"attempt to take the log10 of a negative number");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = BigNum(num.digits_.size() - 1);
|
|
||||||
|
|
||||||
assert(res.is_canonicalized());
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace abacus::bignum
|
} // namespace abacus::bignum
|
||||||
|
|
|
@ -91,10 +91,6 @@ public:
|
||||||
|
|
||||||
friend BigNum sqrt(BigNum const& num);
|
friend BigNum sqrt(BigNum const& num);
|
||||||
|
|
||||||
friend BigNum log2(BigNum const& num);
|
|
||||||
|
|
||||||
friend BigNum log10(BigNum const& num);
|
|
||||||
|
|
||||||
friend bool operator==(BigNum const& lhs, BigNum const& rhs) {
|
friend bool operator==(BigNum const& lhs, BigNum const& rhs) {
|
||||||
return lhs.equal(rhs);
|
return lhs.equal(rhs);
|
||||||
}
|
}
|
||||||
|
@ -119,10 +115,6 @@ public:
|
||||||
return !(lhs < rhs);
|
return !(lhs < rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() {
|
|
||||||
return !is_zero();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_zero() const;
|
bool is_zero() const;
|
||||||
bool is_positive() const;
|
bool is_positive() const;
|
||||||
bool is_negative() const;
|
bool is_negative() const;
|
||||||
|
|
16
src/bignum/meson.build
Normal file
16
src/bignum/meson.build
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
bignum_sources = files(
|
||||||
|
'bignum.cc',
|
||||||
|
'bignum.hh',
|
||||||
|
)
|
||||||
|
|
||||||
|
bignum_inc = include_directories('.')
|
||||||
|
|
||||||
|
bignum_lib = library(
|
||||||
|
'bignum',
|
||||||
|
sources: bignum_sources,
|
||||||
|
)
|
||||||
|
|
||||||
|
bignum = declare_dependency(
|
||||||
|
link_with: bignum_lib,
|
||||||
|
include_directories: bignum_inc,
|
||||||
|
)
|
15
src/meson.build
Normal file
15
src/meson.build
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
abacus_sources = files(
|
||||||
|
'abacus.cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('bignum')
|
||||||
|
subdir('parse')
|
||||||
|
|
||||||
|
abacus = executable(
|
||||||
|
'abacus',
|
||||||
|
sources: abacus_sources,
|
||||||
|
dependencies: [
|
||||||
|
bignum,
|
||||||
|
parse,
|
||||||
|
],
|
||||||
|
)
|
|
@ -1,23 +0,0 @@
|
||||||
find_package(BISON REQUIRED)
|
|
||||||
find_package(FLEX REQUIRED)
|
|
||||||
|
|
||||||
bison_target(parser_sources parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cc DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.hh)
|
|
||||||
flex_target(scanner_sources scanner.ll ${CMAKE_CURRENT_BINARY_DIR}/scanner.cc)
|
|
||||||
add_flex_bison_dependency(scanner_sources parser_sources)
|
|
||||||
|
|
||||||
add_library(parse STATIC
|
|
||||||
parser-driver.cc
|
|
||||||
parser-driver.hh
|
|
||||||
${BISON_parser_sources_OUTPUTS}
|
|
||||||
${FLEX_scanner_sources_OUTPUTS}
|
|
||||||
)
|
|
||||||
target_link_libraries(parse PRIVATE common_options)
|
|
||||||
|
|
||||||
target_link_libraries(parse PRIVATE
|
|
||||||
bignum
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(parse PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
||||||
)
|
|
48
src/parse/meson.build
Normal file
48
src/parse/meson.build
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
flex_binary = find_program('flex', required: true)
|
||||||
|
lexer_sources = custom_target(
|
||||||
|
'lexer_sources',
|
||||||
|
input: 'scanner.ll',
|
||||||
|
output: 'scanner.cc',
|
||||||
|
command: [
|
||||||
|
flex_binary,
|
||||||
|
'-o',
|
||||||
|
'@OUTPUT@',
|
||||||
|
'@INPUT@',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
bison_binary = find_program('bison', required: true)
|
||||||
|
parser_sources = custom_target(
|
||||||
|
'parser_sources',
|
||||||
|
input: 'parser.yy',
|
||||||
|
output: [
|
||||||
|
'parser.cc',
|
||||||
|
'parser.hh',
|
||||||
|
],
|
||||||
|
command: [
|
||||||
|
bison_binary,
|
||||||
|
'@INPUT@',
|
||||||
|
'--output=@OUTPUT0@',
|
||||||
|
'--defines=@OUTPUT1@',
|
||||||
|
'--graph',
|
||||||
|
# FIXME: html output in bison 3.7.90
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
parse_inc = include_directories('.')
|
||||||
|
|
||||||
|
parse_lib = library(
|
||||||
|
'parser',
|
||||||
|
'parser-driver.cc',
|
||||||
|
'parser-driver.hh',
|
||||||
|
lexer_sources,
|
||||||
|
parser_sources,
|
||||||
|
dependencies: [
|
||||||
|
bignum,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
parse = declare_dependency(
|
||||||
|
link_with: parse_lib,
|
||||||
|
include_directories: parse_inc,
|
||||||
|
)
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#include "parser.hh"
|
#include "parser.hh"
|
||||||
|
|
||||||
#include "bignum/bignum.hh"
|
#include "bignum.hh" // FIXME: I would like `bignum/bignum.hh` path instead...
|
||||||
|
|
||||||
namespace abacus::parse {
|
namespace abacus::parse {
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace abacus::parse {
|
||||||
class ParserDriver;
|
class ParserDriver;
|
||||||
} // namespace abacus::parse
|
} // namespace abacus::parse
|
||||||
|
|
||||||
#include "bignum/bignum.hh"
|
#include "bignum.hh" // FIXME: I would like `bignum/bignum.hh` path instead...
|
||||||
}
|
}
|
||||||
|
|
||||||
%code provides {
|
%code provides {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
add_subdirectory(unit)
|
|
1
tests/meson.build
Normal file
1
tests/meson.build
Normal file
|
@ -0,0 +1 @@
|
||||||
|
subdir('unit')
|
|
@ -1,16 +0,0 @@
|
||||||
find_package(GTest)
|
|
||||||
|
|
||||||
if (${GTest_FOUND})
|
|
||||||
include(GoogleTest)
|
|
||||||
|
|
||||||
add_executable(bignum_test bignum.cc)
|
|
||||||
target_link_libraries(bignum_test PRIVATE common_options)
|
|
||||||
|
|
||||||
target_link_libraries(bignum_test PRIVATE
|
|
||||||
bignum
|
|
||||||
GTest::gtest
|
|
||||||
GTest::gtest_main
|
|
||||||
)
|
|
||||||
|
|
||||||
gtest_discover_tests(bignum_test)
|
|
||||||
endif (${GTest_FOUND})
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "bignum/bignum.hh"
|
#include "bignum.hh"
|
||||||
|
|
||||||
using namespace abacus::bignum;
|
using namespace abacus::bignum;
|
||||||
|
|
||||||
|
@ -68,34 +68,6 @@ TEST(BigNum, comparisons_digits) {
|
||||||
EXPECT_GT(ten, nine);
|
EXPECT_GT(ten, nine);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigNum, comparisons_negative) {
|
|
||||||
auto const zero = BigNum(0);
|
|
||||||
auto const minus_one = BigNum(-1);
|
|
||||||
auto const minus_two = BigNum(-2);
|
|
||||||
|
|
||||||
EXPECT_LT(minus_one, zero);
|
|
||||||
EXPECT_LE(minus_one, zero);
|
|
||||||
EXPECT_LT(minus_two, minus_one);
|
|
||||||
EXPECT_LE(minus_two, minus_one);
|
|
||||||
EXPECT_LE(minus_one, minus_one);
|
|
||||||
EXPECT_GE(minus_two, minus_two);
|
|
||||||
|
|
||||||
EXPECT_GT(zero, minus_one);
|
|
||||||
EXPECT_GE(zero, minus_one);
|
|
||||||
EXPECT_GT(minus_one, minus_two);
|
|
||||||
EXPECT_GE(minus_one, minus_two);
|
|
||||||
EXPECT_GE(minus_one, minus_one);
|
|
||||||
EXPECT_GE(minus_two, minus_two);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BigNum, comparisons_digits_negative) {
|
|
||||||
auto const minus_nine = BigNum(-9);
|
|
||||||
auto const minus_ten = BigNum(-10);
|
|
||||||
|
|
||||||
EXPECT_LT(minus_ten, minus_nine);
|
|
||||||
EXPECT_GT(minus_nine, minus_ten);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BigNum, unary) {
|
TEST(BigNum, unary) {
|
||||||
auto const zero = BigNum(0);
|
auto const zero = BigNum(0);
|
||||||
auto const one = BigNum(1);
|
auto const one = BigNum(1);
|
||||||
|
@ -404,44 +376,3 @@ TEST(BigNum, sqrt) {
|
||||||
EXPECT_EQ(sqrt(ninety_nine), nine);
|
EXPECT_EQ(sqrt(ninety_nine), nine);
|
||||||
EXPECT_EQ(sqrt(hundred), ten);
|
EXPECT_EQ(sqrt(hundred), ten);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(BigNum, log2) {
|
|
||||||
auto const zero = BigNum(0);
|
|
||||||
auto const one = BigNum(1);
|
|
||||||
auto const two = BigNum(2);
|
|
||||||
auto const three = BigNum(3);
|
|
||||||
auto const four = BigNum(4);
|
|
||||||
auto const five = BigNum(5);
|
|
||||||
auto const seven = BigNum(7);
|
|
||||||
auto const eight = BigNum(8);
|
|
||||||
auto const nine = BigNum(9);
|
|
||||||
|
|
||||||
EXPECT_EQ(log2(one), zero);
|
|
||||||
EXPECT_EQ(log2(two), one);
|
|
||||||
EXPECT_EQ(log2(three), one);
|
|
||||||
EXPECT_EQ(log2(four), two);
|
|
||||||
EXPECT_EQ(log2(five), two);
|
|
||||||
EXPECT_EQ(log2(seven), two);
|
|
||||||
EXPECT_EQ(log2(eight), three);
|
|
||||||
EXPECT_EQ(log2(nine), three);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BigNum, log10) {
|
|
||||||
auto const zero = BigNum(0);
|
|
||||||
auto const one = BigNum(1);
|
|
||||||
auto const two = BigNum(2);
|
|
||||||
auto const nine = BigNum(9);
|
|
||||||
auto const ten = BigNum(10);
|
|
||||||
auto const eleven = BigNum(11);
|
|
||||||
auto const ninety_nine = BigNum(99);
|
|
||||||
auto const hundred = BigNum(100);
|
|
||||||
auto const hundred_one = BigNum(101);
|
|
||||||
|
|
||||||
EXPECT_EQ(log10(one), zero);
|
|
||||||
EXPECT_EQ(log10(nine), zero);
|
|
||||||
EXPECT_EQ(log10(ten), one);
|
|
||||||
EXPECT_EQ(log10(eleven), one);
|
|
||||||
EXPECT_EQ(log10(ninety_nine), one);
|
|
||||||
EXPECT_EQ(log10(hundred), two);
|
|
||||||
EXPECT_EQ(log10(hundred_one), two);
|
|
||||||
}
|
|
||||||
|
|
26
tests/unit/meson.build
Normal file
26
tests/unit/meson.build
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
gtest = dependency(
|
||||||
|
'gtest',
|
||||||
|
main: true,
|
||||||
|
required: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
if gtest.found()
|
||||||
|
unit_test_sources = files(
|
||||||
|
'bignum.cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
unit_tests = executable(
|
||||||
|
'unit_tests',
|
||||||
|
sources: unit_test_sources,
|
||||||
|
dependencies: [
|
||||||
|
bignum,
|
||||||
|
gtest,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'unit tests',
|
||||||
|
unit_tests,
|
||||||
|
protocol: 'gtest',
|
||||||
|
)
|
||||||
|
endif
|
Loading…
Reference in a new issue