From a7a9bd72d6ddd7522d6069e771c737550d6fbfbb Mon Sep 17 00:00:00 2001 From: trizen Date: Fri, 27 Aug 2021 18:32:01 +0300 Subject: [PATCH] - Added the `Fraction(a,b)` built-in class. Represents a symbolic fraction: a/b, where `a` and `b` can be any objects. - Implemented division of two polynomials, by using the `Fraction()` newly added class. Example: var a = Poly([1,2,3]) var b = Poly([3,4,5]) say a/b Outputs: Fraction(x^2 + 2*x + 3, 3*x^2 + 4*x + 5) --- MANIFEST | 3 + lib/Sidef/Deparse/Perl.pm | 1 + lib/Sidef/Deparse/Sidef.pm | 1 + lib/Sidef/Parser.pm | 2 + lib/Sidef/Types/Number/Fraction.pm | 243 +++++++++++++++ lib/Sidef/Types/Number/Fraction.pod | 311 ++++++++++++++++++++ lib/Sidef/Types/Number/Number.pm | 20 +- lib/Sidef/Types/Number/Polynomial.pm | 72 ++++- lib/Sidef/Types/Number/Polynomial.pod | 18 ++ scripts/Tests/fraction_class.sf | 117 ++++++++ scripts/Tests/polynomial.sf | 37 ++- scripts/Tests/symbolic_fractions.sf | 56 ++-- scripts/symmetric_summation_of_fractions.sf | 11 +- 13 files changed, 831 insertions(+), 61 deletions(-) create mode 100644 lib/Sidef/Types/Number/Fraction.pm create mode 100644 lib/Sidef/Types/Number/Fraction.pod create mode 100644 scripts/Tests/fraction_class.sf diff --git a/MANIFEST b/MANIFEST index 0c3278e02..f2d75fa0d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -72,6 +72,8 @@ lib/Sidef/Types/Null/Null.pm lib/Sidef/Types/Null/Null.pod lib/Sidef/Types/Number/Complex.pm lib/Sidef/Types/Number/Complex.pod +lib/Sidef/Types/Number/Fraction.pm +lib/Sidef/Types/Number/Fraction.pod lib/Sidef/Types/Number/Gauss.pm lib/Sidef/Types/Number/Gauss.pod lib/Sidef/Types/Number/Mod.pm @@ -779,6 +781,7 @@ scripts/Tests/for_in_with_user_objects.sf scripts/Tests/for_two_vars.sf scripts/Tests/for_var_in_array.sf scripts/Tests/forward_difference.sf +scripts/Tests/fraction_class.sf scripts/Tests/func_scoping.sf scripts/Tests/function_composition.sf scripts/Tests/functional_modules.sf diff --git a/lib/Sidef/Deparse/Perl.pm b/lib/Sidef/Deparse/Perl.pm index 8a2da82bf..e700540f5 100644 --- a/lib/Sidef/Deparse/Perl.pm +++ b/lib/Sidef/Deparse/Perl.pm @@ -62,6 +62,7 @@ package Sidef::Deparse::Perl { Sidef::DataTypes::Number::Quaternion Sidef::Types::Number::Quaternion Sidef::DataTypes::Number::Complex Sidef::Types::Number::Complex Sidef::DataTypes::Number::Polynomial Sidef::Types::Number::Polynomial + Sidef::DataTypes::Number::Fraction Sidef::Types::Number::Fraction Sidef::DataTypes::Range::Range Sidef::Types::Range::Range Sidef::DataTypes::Range::RangeNumber Sidef::Types::Range::RangeNumber Sidef::DataTypes::Range::RangeString Sidef::Types::Range::RangeString diff --git a/lib/Sidef/Deparse/Sidef.pm b/lib/Sidef/Deparse/Sidef.pm index c1f7e0a71..b13228003 100644 --- a/lib/Sidef/Deparse/Sidef.pm +++ b/lib/Sidef/Deparse/Sidef.pm @@ -35,6 +35,7 @@ package Sidef::Deparse::Sidef { Sidef::DataTypes::Number::Quaternion Quaternion Sidef::DataTypes::Number::Complex Complex Sidef::DataTypes::Number::Polynomial Polynomial + Sidef::DataTypes::Number::Fraction Fraction Sidef::DataTypes::Range::Range Range Sidef::DataTypes::Range::RangeNumber RangeNum Sidef::DataTypes::Range::RangeString RangeStr diff --git a/lib/Sidef/Parser.pm b/lib/Sidef/Parser.pm index e20f636f0..239fddea0 100644 --- a/lib/Sidef/Parser.pm +++ b/lib/Sidef/Parser.pm @@ -75,6 +75,7 @@ package Sidef::Parser { | Quadratic\b (?{ state $x = bless({}, 'Sidef::DataTypes::Number::Quadratic') }) | Quaternion\b (?{ state $x = bless({}, 'Sidef::DataTypes::Number::Quaternion') }) | Poly(?:nomial)?\b (?{ state $x = bless({}, 'Sidef::DataTypes::Number::Polynomial') }) + | Frac(?:tion)?\b (?{ state $x = bless({}, 'Sidef::DataTypes::Number::Fraction') }) | Inf\b (?{ state $x = Sidef::Types::Number::Number->inf }) | NaN\b (?{ state $x = Sidef::Types::Number::Number->nan }) | Infi\b (?{ state $x = Sidef::Types::Number::Complex->new(0, Sidef::Types::Number::Number->inf) }) @@ -208,6 +209,7 @@ package Sidef::Parser { Str String Num Number Poly Polynomial + Frac Fraction Mod Gauss Quadratic diff --git a/lib/Sidef/Types/Number/Fraction.pm b/lib/Sidef/Types/Number/Fraction.pm new file mode 100644 index 000000000..b3e8be867 --- /dev/null +++ b/lib/Sidef/Types/Number/Fraction.pm @@ -0,0 +1,243 @@ +package Sidef::Types::Number::Fraction { + + use utf8; + use 5.016; + + use parent qw( + Sidef::Types::Number::Number + ); + + use overload + q{bool} => \&to_n, + q{0+} => \&to_n, + q{""} => \&__stringify__, + q{${}} => \&to_n; + + sub new { + my (undef, $n, $m) = @_; + + $n //= Sidef::Types::Number::Number::ZERO; + $m //= Sidef::Types::Number::Number::ONE; + + bless {a => $n, b => $m}; + } + + *call = \&new; + + sub to_n { + my ($x) = @_; + my $r = $x->{a}->to_n->div($x->{b}->to_n); + + if (ref($r) eq __PACKAGE__) { # probably should die here? + return Sidef::Types::Number::Number->new($x->{a})->div(Sidef::Types::Number::Number->new($x->{b})); + } + + return $r; + } + + *lift = \&to_n; + *__boolify__ = \&to_n; + *__numify__ = \&to_n; + + sub __stringify__ { + my ($x) = @_; + "Fraction($x->{a}, $x->{b})"; + } + + sub to_s { + my ($x) = @_; + Sidef::Types::String::String->new($x->__stringify__); + } + + *dump = \&to_s; + + sub nu { + $_[0]->{a}; + } + + *numerator = \ν + + sub de { + $_[0]->{b}; + } + + *denominator = \&de; + + sub nude { + ($_[0]->{a}, $_[0]->{b}); + } + + sub neg { + my ($x) = @_; + __PACKAGE__->new($x->{a}->neg, $x->{b}); + } + + sub dec { + my ($x) = @_; + __PACKAGE__->new($x->{a}->sub($x->{b}), $x->{b},); + } + + sub inc { + my ($x) = @_; + __PACKAGE__->new($x->{a}->add($x->{b}), $x->{b},); + } + + sub add { + my ($x, $y) = @_; + + if (ref($y) eq __PACKAGE__) { + return __PACKAGE__->new($x->{a}->mul($y->{b})->add($y->{a}->mul($x->{b})), $x->{b}->mul($y->{b})); + } + + __PACKAGE__->new($x->{a}->add($y->mul($x->{b})), $x->{b}); + } + + sub sub { + my ($x, $y) = @_; + $x->add($y->neg); + } + + sub mul { + my ($x, $y) = @_; + + if (ref($y) eq __PACKAGE__) { + return __PACKAGE__->new($x->{a}->mul($y->{a}), $x->{b}->mul($y->{b}),); + } + + __PACKAGE__->new($x->{a}->mul($y), $x->{b}); + } + + sub div { + my ($x, $y) = @_; + + if (ref($y) eq __PACKAGE__) { + return __PACKAGE__->new($x->{a}->mul($y->{b}), $x->{b}->mul($y->{a}),); + } + + __PACKAGE__->new($x->{a}, $x->{b}->mul($y)); + } + + sub pow { + my ($x, $n) = @_; + + if ($n->is_neg) { + my $abs_n = $n->neg; + return __PACKAGE__->new($x->{b}->pow($abs_n), $x->{a}->pow($abs_n),); + } + + __PACKAGE__->new($x->{a}->pow($n), $x->{b}->pow($n)); + } + + sub lsft { + my ($x, $n) = @_; + __PACKAGE__->new($x->{a}->lsft($n), $x->{b}); + } + + sub rsft { + my ($x, $n) = @_; + __PACKAGE__->new($x->{a}, $x->{b}->lsft($n)); + } + + sub floor { + my ($x) = @_; + + my $y = $x->{a}->div($x->{b}); + + if (ref($y) ne __PACKAGE__) { + return $y->floor; + } + + $x->{a}->idiv_floor($x->{b}); + } + + sub ceil { + my ($x) = @_; + + my $y = $x->{a}->div($x->{b}); + + if (ref($y) ne __PACKAGE__) { + return $y->ceil; + } + + $x->{a}->idiv_ceil($x->{b}); + } + + sub round { + my ($x) = @_; + + my $y = $x->{a}->div($x->{b}); + + if (ref($y) ne __PACKAGE__) { + return $y->round; + } + + $x->{a}->idiv_round($x->{b}); + } + + sub trunc { + my ($x) = @_; + + my $y = $x->{a}->div($x->{b}); + + if (ref($y) ne __PACKAGE__) { + return $y->trunc; + } + + $x->{a}->idiv_trunc($x->{b}); + } + + sub mod { + my ($x, $y) = @_; + + if (ref($y) ne __PACKAGE__) { + return __PACKAGE__->new( + Sidef::Types::Number::Mod->new($x->{a}, $y), + Sidef::Types::Number::Mod->new($x->{b}, $y), + ); + } + + $x->sub($y->mul($x->div($y)->floor)); + } + + { + no strict 'refs'; + + foreach my $method (qw(eq ne lt le gt ge cmp or xor and)) { + *{__PACKAGE__ . '::' . $method} = sub { + my ($x, $y) = @_; + + if (ref($y) ne __PACKAGE__) { + $y = __PACKAGE__->new($y); + } + + $x->{a}->mul($y->{b})->$method($x->{b}->mul($y->{a})); + }; + } + + *{__PACKAGE__ . '::' . '%'} = \&mod; + *{__PACKAGE__ . '::' . '/'} = \÷ + *{__PACKAGE__ . '::' . '÷'} = \÷ + *{__PACKAGE__ . '::' . '*'} = \&mul; + *{__PACKAGE__ . '::' . '+'} = \&add; + *{__PACKAGE__ . '::' . '-'} = \⊂ + *{__PACKAGE__ . '::' . '**'} = \&pow; + *{__PACKAGE__ . '::' . '++'} = \&inc; + *{__PACKAGE__ . '::' . '--'} = \&dec; + *{__PACKAGE__ . '::' . '<'} = \< + *{__PACKAGE__ . '::' . '>'} = \> + *{__PACKAGE__ . '::' . '&'} = \∧ + *{__PACKAGE__ . '::' . '|'} = \∨ + *{__PACKAGE__ . '::' . '^'} = \&xor; + *{__PACKAGE__ . '::' . '<<'} = \&lsft; + *{__PACKAGE__ . '::' . '>>'} = \&rsft; + *{__PACKAGE__ . '::' . '<=>'} = \&cmp; + *{__PACKAGE__ . '::' . '<='} = \≤ + *{__PACKAGE__ . '::' . '≤'} = \≤ + *{__PACKAGE__ . '::' . '>='} = \≥ + *{__PACKAGE__ . '::' . '≥'} = \≥ + *{__PACKAGE__ . '::' . '=='} = \&eq; + *{__PACKAGE__ . '::' . '!='} = \≠ + } +} + +1 diff --git a/lib/Sidef/Types/Number/Fraction.pod b/lib/Sidef/Types/Number/Fraction.pod new file mode 100644 index 000000000..6d7a333f3 --- /dev/null +++ b/lib/Sidef/Types/Number/Fraction.pod @@ -0,0 +1,311 @@ + +=encoding utf8 + +=head1 NAME + +Sidef::Types::Number::Fraction + +=head1 DESCRIPTION + +This class implements ... + +=head1 SYNOPSIS + +var obj = Fraction(...) + + +=head1 INHERITS + +Inherits methods from: + + * Sidef::Types::Number::Number + +=head1 METHODS + +=head2 != + + a != b + +Returns the + +Aliases: I + +=cut + +=head2 & + + a & b + +Returns the + +Aliases: I + +=cut + +=head2 * + + a * b + +Returns the + +Aliases: I + +=cut + +=head2 ** + + a ** b + +Returns the + +Aliases: I + +=cut + +=head2 + + + a + b + +Returns the + +Aliases: I + +=cut + +=head2 ++ + + a ++ b + +Returns the + +Aliases: I + +=cut + +=head2 - + + a - b + +Returns the + +Aliases: I + +=cut + +=head2 -- + + a -- b + +Returns the + +Aliases: I + +=cut + +=head2 / + + a / b + +Returns the + +Aliases: I<÷>, I
+ +=cut + +=head2 < + + a < b + +Returns the + +Aliases: I + +=cut + +=head2 << + + a << b + +Returns the + +Aliases: I + +=cut + +=head2 <=> + + a <=> b + +Returns the + +Aliases: I + +=cut + +=head2 == + + a == b + +Returns the + +Aliases: I + +=cut + +=head2 > + + a > b + +Returns the + +Aliases: I + +=cut + +=head2 >> + + a >> b + +Returns the + +Aliases: I + +=cut + +=head2 ^ + + a ^ b + +Returns the + +Aliases: I + +=cut + +=head2 | + + a | b + +Returns the + +Aliases: I + +=cut + +=head2 ≤ + + a ≤ b + +Returns the + +Aliases: I=>, I + +=cut + +=head2 ≥ + + a ≥ b + +Returns the + +Aliases: I=>, I + +=cut + +=head2 ceil + + Fraction.ceil() + +Returns the + +=cut + +=head2 de + + Fraction.de() + +Returns the + +Aliases: I + +=cut + +=head2 floor + + Fraction.floor() + +Returns the + +=cut + +=head2 neg + + Fraction.neg() + +Returns the + +=cut + +=head2 new + + Fraction.new() + +Returns the + +Aliases: I + +=cut + +=head2 nu + + Fraction.nu() + +Returns the + +Aliases: I + +=cut + +=head2 nude + + Fraction.nude() + +Returns the + +=cut + +=head2 round + + Fraction.round() + +Returns the + +=cut + +=head2 to_n + + Fraction.to_n() + +Returns the + +Aliases: I + +=cut + +=head2 to_s + + Fraction.to_s() + +Returns the + +Aliases: I + +=cut + +=head2 trunc + + Fraction.trunc() + +Returns the + +=cut diff --git a/lib/Sidef/Types/Number/Number.pm b/lib/Sidef/Types/Number/Number.pm index a3916bb55..ccf6fd6b3 100644 --- a/lib/Sidef/Types/Number/Number.pm +++ b/lib/Sidef/Types/Number/Number.pm @@ -1715,6 +1715,7 @@ package Sidef::Types::Number::Number { if ( $ref eq 'Sidef::Types::Number::Mod' or $ref eq 'Sidef::Types::Number::Gauss' + or $ref eq 'Sidef::Types::Number::Fraction' or $ref eq 'Sidef::Types::Number::Quadratic' or $ref eq 'Sidef::Types::Number::Quaternion' or $ref eq 'Sidef::Types::Number::Polynomial') { @@ -1869,7 +1870,9 @@ package Sidef::Types::Number::Number { return $ref->new($x, $y->{m})->sub($y); } - if ($ref eq 'Sidef::Types::Number::Gauss') { + if ( $ref eq 'Sidef::Types::Number::Gauss' + or $ref eq 'Sidef::Types::Number::Quaternion' + or $ref eq 'Sidef::Types::Number::Fraction') { return $ref->new($x)->sub($y); } @@ -1877,10 +1880,6 @@ package Sidef::Types::Number::Number { return $ref->new($x, ZERO, $y->{w})->sub($y); } - if ($ref eq 'Sidef::Types::Number::Quaternion') { - return $ref->new($x)->sub($y); - } - if ($ref eq 'Sidef::Types::Number::Polynomial') { return $ref->new(0 => $x)->sub($y); } @@ -2014,6 +2013,7 @@ package Sidef::Types::Number::Number { if ( $ref eq 'Sidef::Types::Number::Mod' or $ref eq 'Sidef::Types::Number::Gauss' + or $ref eq 'Sidef::Types::Number::Fraction' or $ref eq 'Sidef::Types::Number::Quadratic' or $ref eq 'Sidef::Types::Number::Quaternion' or $ref eq 'Sidef::Types::Number::Polynomial' @@ -2190,20 +2190,18 @@ package Sidef::Types::Number::Number { return $ref->new($x, $y->{m})->div($y); } - if ($ref eq 'Sidef::Types::Number::Gauss') { - return $ref->new($x)->div($y); - } - if ($ref eq 'Sidef::Types::Number::Quadratic') { return $ref->new($x, ZERO, $y->{w})->div($y); } - if ($ref eq 'Sidef::Types::Number::Quaternion') { + if ( $ref eq 'Sidef::Types::Number::Gauss' + or $ref eq 'Sidef::Types::Number::Fraction' + or $ref eq 'Sidef::Types::Number::Quaternion') { return $ref->new($x)->div($y); } if ($ref eq 'Sidef::Types::Number::Polynomial') { - return $ref->new($x)->div($y); + return $ref->new(0 => $x)->div($y); } _valid(\$y); diff --git a/lib/Sidef/Types/Number/Polynomial.pm b/lib/Sidef/Types/Number/Polynomial.pm index 937c72dd2..6452e668f 100644 --- a/lib/Sidef/Types/Number/Polynomial.pm +++ b/lib/Sidef/Types/Number/Polynomial.pm @@ -55,6 +55,20 @@ package Sidef::Types::Number::Polynomial { *call = \&new; + sub to_n { + my ($x) = @_; + + my $d = scalar keys(%$x); + + return Sidef::Types::Number::Number::ZERO if ($d == 0); + + if ($d == 1 and exists $x->{0}) { + return $x->{0}; + } + + return $x; + } + sub __dump__ { my ($x) = @_; 'Polynomial(' . join(", ", map { join(' => ', $_, $x->{$_}->dump) } sort { $a <=> $b } CORE::keys %$x) . ')'; @@ -119,7 +133,7 @@ package Sidef::Types::Number::Polynomial { sub keys { my ($x) = @_; - Sidef::Types::Array::Array->new(map { Sidef::Types::Number::Number::_set_int($_) } CORE::keys(%$x)); + Sidef::Types::Array::Array->new(map { Sidef::Types::Number::Number::_set_int($_) } sort { $a <=> $b } CORE::keys(%$x)); } *exponents = \&keys; @@ -135,7 +149,7 @@ package Sidef::Types::Number::Polynomial { [ map { Sidef::Types::Array::Array->new([Sidef::Types::Number::Number::_set_int($_), $x->{$_}]) - } CORE::keys(%$x) + } sort { $a <=> $b } CORE::keys(%$x) ] ); } @@ -151,7 +165,7 @@ package Sidef::Types::Number::Polynomial { if (ref($y) eq __PACKAGE__) { return __PACKAGE__->new((map { $_ => (exists($y->{$_}) ? $x->{$_}->add($y->{$_}) : $x->{$_}) } CORE::keys %$x), - (map { exists($x->{$_}) ? () : ($_ => $y->{$_}) } CORE::keys %$y),); + (map { exists($x->{$_}) ? () : ($_ => $y->{$_}) } CORE::keys %$y)); } if (not exists $x->{0}) { @@ -215,9 +229,17 @@ package Sidef::Types::Number::Polynomial { sub divmod { my ($x, $y) = @_; + # TODO: optimize this method for better performance. + my @keys_x = sort { $b <=> $a } CORE::keys %$x; my @keys_y = sort { $b <=> $a } CORE::keys %$y; + @keys_x + || return (__PACKAGE__->new(), __PACKAGE__->new()); + + @keys_y + || return (__PACKAGE__->new(0 => Sidef::Types::Number::Number::inf()), __PACKAGE__->new()); + my $key_y = shift @keys_y; my $yc = $y->{$key_y}; @@ -239,6 +261,23 @@ package Sidef::Types::Number::Polynomial { return ($quot, $x); } + sub idiv { + my ($x, $y) = @_; + + if (ref($y) ne __PACKAGE__) { + $y = __PACKAGE__->new(0 => $y); + } + + my ($quot, $rem) = $x->divmod($y); + return $quot; + } + + # Probably not right? + *idiv_ceil = \&idiv; + *idiv_trunc = \&idiv; + *idiv_round = \&idiv; + *idiv_floor = \&idiv; + sub div { my ($x, $y) = @_; @@ -250,7 +289,11 @@ package Sidef::Types::Number::Polynomial { return $quot; } - # TODO: implement division by another polynomial when the remainder != 0 + return Sidef::Types::Number::Fraction->new($quot->mul($y)->add($rem), $y); + } + + if ($y->is_zero) { + return __PACKAGE__->new(0 => Sidef::Types::Number::Number::inf()); } $x->mul($y->inv); @@ -279,22 +322,21 @@ package Sidef::Types::Number::Polynomial { sub mod { my ($x, $y) = @_; - if (ref($y) eq 'Sidef::Types::Number::Number') { - return __PACKAGE__->new(map { $_ => $x->{$_}->mod($y) } CORE::keys %$x); - } + if (ref($y) eq __PACKAGE__) { - # mod(a, b) = a - b * floor(a/b) - # $x->sub($y->mul($x->div($y)->floor)); + # mod(a, b) = a - b * floor(a/b) + # return $x->sub($y->mul($x->div($y)->floor)); - my ($quot, $rem) = $x->divmod($y); - return $rem; + my ($quot, $rem) = $x->divmod($y); + return $rem; + } + + __PACKAGE__->new(map { $_ => Sidef::Types::Number::Mod->new($x->{$_}, $y) } CORE::keys %$x); } sub inv { my ($x) = @_; - - # TODO: implement - ...; + Sidef::Types::Number::Fraction->new(Sidef::Types::Number::Number::ONE, $x); } sub invmod { @@ -381,7 +423,7 @@ package Sidef::Types::Number::Polynomial { } if ($negative_power) { - $c = $c->invmod($m); + $c = $c->inv; } return $c; diff --git a/lib/Sidef/Types/Number/Polynomial.pod b/lib/Sidef/Types/Number/Polynomial.pod index e18e22f10..42de8bf37 100644 --- a/lib/Sidef/Types/Number/Polynomial.pod +++ b/lib/Sidef/Types/Number/Polynomial.pod @@ -236,6 +236,16 @@ Returns the =cut +=head2 idiv + + Polynomial.idiv() + +Returns the + +Aliases: I, I, I, I + +=cut + =head2 inv Polynomial.inv() @@ -328,6 +338,14 @@ Returns the =cut +=head2 to_n + + Polynomial.to_n() + +Returns the + +=cut + =head2 to_s Polynomial.to_s() diff --git a/scripts/Tests/fraction_class.sf b/scripts/Tests/fraction_class.sf new file mode 100644 index 000000000..6622504a0 --- /dev/null +++ b/scripts/Tests/fraction_class.sf @@ -0,0 +1,117 @@ +#!/usr/bin/ruby + +# Tests for the Fraction class. + +do { + var r = 42+Fraction(3,4) + assert_eq(r.nu, 171) + assert_eq(r.de, 4) + assert_eq(42 + 3/4, r.nu/r.de) +} + +do { + var r = 42*Fraction(3, 4) + assert_eq(r.nu, 42*3) + assert_eq(r.de, 4) +} + +do { + var r = 1/Fraction(3,4) + assert_eq(r.nu, 4) + assert_eq(r.de, 3) +} + +do { + var r = 12-Fraction(3, 4) + assert_eq(r.nu, 45) + assert_eq(r.de, 4) +} + +do { + # + ## sum(f(n)) = e, as n->oo. + # + func f((0)) { Fraction(1, 1) } + func f(n) { f(n-1) / n } + + assert_eq(f(10).de, 10!) + + func nu(n) { (-1)**n } + func de(n) { (2*n + 1)**2 } + + # + ## sum(nu(n)/de(n)) = Catalan's constant, as n->oo. + # + + var sum = Fraction() + for i in (0 .. 5) { + sum += Fraction(nu(i), de(i)) + } + + assert_eq(sum.nu, 98607816) + assert_eq(sum.de, 108056025) +} + +do { + func num(n) { n**0 } + func den(n) { n**2 } + + var from = 1 + var to = 10 + + var sum = Fraction() + for i in (from .. to) { + sum += Fraction(num(i), den(i)) + } + + assert_eq(sum, Fraction(20407635072000, 13168189440000)) +} + +assert(Fraction(Poly([3,4]), Poly([9,12,13])) <= Fraction(Poly([4,5]))) + +assert_eq(Fraction(3,6), Fraction(5,10)) +assert_ne(Fraction(3,5), Fraction(5,10)) + +assert(!(Fraction(3,5) == Fraction(5,10))) +assert(Fraction(3,5) != Fraction(5,10)) + +assert(Fraction(3,5) > Fraction(5,10)) +assert(Fraction(3,7) < Fraction(5,10)) + +assert(Fraction(3,5) >= Fraction(5,10)) +assert(Fraction(3,7) <= Fraction(5,10)) + +assert(Fraction(3,6) <= Fraction(5,10)) +assert(Fraction(3,6) >= Fraction(5,10)) + +assert_eq(Fraction(40, 60), Fraction(2, 3)) +assert_eq(Fraction(40, 60), 2/3) + +assert_ne(Fraction(3,5), 3/4) + +assert(Fraction(3,5) > 3/6) +assert(Fraction(3,7) < 3/6) + +assert_eq(Fraction(3,7) <=> 3/6, -1) +assert_eq(Fraction(3,6) <=> 3/6, 0) +assert_eq(Fraction(3,5) <=> 3/6, 1) + +assert_eq(Fraction(3,4) << 10, (3/4) * 2**10) +assert_eq(Fraction(3,4) >> 10, (3/4) / 2**10) + +assert_eq(Fraction(3,4).inc, 3/4 + 1) +assert_eq(Fraction(3,4).dec, 3/4 - 1) + +assert_eq(Fraction(15,9).floor, floor(15/9)) +assert_eq(Fraction(15,9).ceil, ceil(15/9)) +assert_eq(Fraction(15,9).round, round(15/9)) +assert_eq(Fraction(15,9).int, int(15/9)) +assert_eq(Fraction(15,9).trunc, trunc(15/9)) + +assert_eq(Fraction(215/7,9).floor, floor((215/7)/9)) +assert_eq(Fraction(188/7,9).ceil, ceil((188/7)/9)) +assert_eq(Fraction(160/7,9).floor, floor((160/7)/9)) +assert_eq(Fraction(160/7,9).ceil, ceil((160/7)/9)) +assert_eq(Fraction(160/7,9).round, round((160/7)/9)) + +say "** Test passed!" diff --git a/scripts/Tests/polynomial.sf b/scripts/Tests/polynomial.sf index 498f16c3d..4cb3ac714 100644 --- a/scripts/Tests/polynomial.sf +++ b/scripts/Tests/polynomial.sf @@ -96,7 +96,7 @@ with (Poly([1,2,3,4])) {|p| Polynomial(0 => 1, 2 => 2, 3 => 1) ) - assert_eq(p % 3 -> to_s, "x^3 + 2*x^2 + 1") + assert_eq(p % 97 -> eval(6), Mod(19, 97)) assert_eq( Poly([0,0,5]), @@ -191,4 +191,39 @@ assert_eq( '1061*x^5 + 1126*x^4 + 591*x^3 + 613*x^2 + 17*x + 2982' ) +with (Poly(1 => 1)) {|x| + + assert_eq( + Poly([3,4,5])**(-2), + Fraction(1, 9*x**4 + 24*x**3 + 46*x**2 + 40*x + 25) + ) + + var p = Poly([5,7,11]) + + assert_eq(p.inv, p**(-1)) + + assert_eq(p/0, Inf) + assert_eq(p/Poly(), Inf) + + assert_eq(0/p, 0) + assert_eq(1/p, p.inv) + assert_eq(2/p, 2*p.inv) + + assert_eq(p.powmod(+5, 97), lift(Mod(p, 97)**+5)) + assert_eq(p.powmod(-5, 97), lift(Mod(p, 97)**-5)) + + assert_eq(Mod(p.powmod(+6, 97), 97), Mod(p, 97)**+6) + assert_eq(Mod(p.powmod(-6, 97), 97), Mod(p, 97)**-6) + + assert_eq(p.powmod(+6, 97).eval(42), Mod(85, 97)) + assert_eq([p.powmod(-6, 97).nude].map { .kind_of(Polynomial) ? .eval(42) : _ }.reduce('/'), 1/577294259220123291015625) + + var a = Poly([11,1,0,3,5]) + var b = Poly([3,0,14]) + + assert_eq(a/b, Fraction(11*x**4 + x**3 + 3*x + 5, 3*x**2 + 14)) + assert_eq(floor(a/b), idiv(a,b)) + assert_eq(floor(Fraction(a,b)), (11/3)*x**2 + (1/3)*x - 154/9) +} + say "** Test passed!" diff --git a/scripts/Tests/symbolic_fractions.sf b/scripts/Tests/symbolic_fractions.sf index f9a83d799..ecffd8610 100644 --- a/scripts/Tests/symbolic_fractions.sf +++ b/scripts/Tests/symbolic_fractions.sf @@ -1,17 +1,17 @@ #!/usr/bin/ruby # -## A very basic Fraction() class, implementing a few symbolic relations. +## A very basic MyFraction() class, implementing a few symbolic relations. # -class Fraction(num, den) { +class MyFraction(num, den) { method +(Number o) { - self + Fraction(o, 1) + self + MyFraction(o, 1) } - method +(Fraction o) { - Fraction( + method +(MyFraction o) { + MyFraction( num*o.den + o.num*den, den*o.den ) @@ -21,27 +21,27 @@ class Fraction(num, den) { self + -o } - method -(Fraction o) { + method -(MyFraction o) { self + -o } method *(Number o) { - Fraction(num*o, den) + MyFraction(num*o, den) } - method *(Fraction o) { - Fraction(num*o.num, den*o.den) + method *(MyFraction o) { + MyFraction(num*o.num, den*o.den) } method /(Number o) { - Fraction( + MyFraction( num, den * o ) } - method /(Fraction o) { - Fraction( + method /(MyFraction o) { + MyFraction( num * o.den, den * o.num, ) @@ -50,62 +50,62 @@ class Fraction(num, den) { method **(Number o) { if (o < 0) { var a = o.abs - Fraction(den**a, num**a) + MyFraction(den**a, num**a) } else { - Fraction(num**o, den**o) + MyFraction(num**o, den**o) } } method neg { - Fraction(-num, den) + MyFraction(-num, den) } method to_s { - "Fraction(#{num}, #{den})" + "MyFraction(#{num}, #{den})" } } class Number { - method +(Fraction o) { + method +(MyFraction o) { o + self } - method -(Fraction o) { + method -(MyFraction o) { -o + self } - method *(Fraction o) { + method *(MyFraction o) { o * self } - method /(Fraction o) { + method /(MyFraction o) { o**(-1) * self } } -var r = 42+Fraction(3,4) +var r = 42+MyFraction(3,4) assert_eq(r.num, 171) assert_eq(r.den, 4) assert_eq(42 + 3/4, r.num/r.den) -r = 42*Fraction(3, 4) +r = 42*MyFraction(3, 4) assert_eq(r.num, 42*3) assert_eq(r.den, 4) -r = 1/Fraction(3,4) +r = 1/MyFraction(3,4) assert_eq(r.num, 4) assert_eq(r.den, 3) -r = 12-Fraction(3, 4) +r = 12-MyFraction(3, 4) assert_eq(r.num, 45) assert_eq(r.den, 4) # ## sum(f(n)) = e, as n->oo. # -func f((0)) { Fraction(1, 1) } +func f((0)) { MyFraction(1, 1) } func f(n) { f(n-1) / n } assert_eq(f(10).den, 10!) @@ -116,10 +116,10 @@ func den(n) { (2*n + 1)**2 } # ## sum(num(n)/den(n)) = Catalan's constant, as n->oo. # -var sum +var sum = MyFraction(0, 1) + for i in (0 .. 5) { - var f = Fraction(num(i), den(i)) - defined(sum) ? (sum += f) : (sum = f) + sum += MyFraction(num(i), den(i)) say sum } diff --git a/scripts/symmetric_summation_of_fractions.sf b/scripts/symmetric_summation_of_fractions.sf index 1d6d070db..46ff099ba 100644 --- a/scripts/symmetric_summation_of_fractions.sf +++ b/scripts/symmetric_summation_of_fractions.sf @@ -1,9 +1,9 @@ #!/usr/bin/ruby -class Fraction(num, den) { +class MyFraction(num, den) { - method +(Fraction arg) { - Fraction( + method +(MyFraction arg) { + MyFraction( self.num*arg.den + arg.num*self.den, self.den*arg.den ) @@ -20,9 +20,8 @@ func den(n) { n**2 } var from = 1 var to = 10 -var sum +var sum = MyFraction(0, 1) for i in (from .. to) { - var f = Fraction(num(i), den(i)) - defined(sum) ? (sum += f) : (sum = f) + sum += MyFraction(num(i), den(i)) say sum }