#  Copyright (c) 1997-2018
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.polymake.org
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program 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 General Public License for more details.
#-------------------------------------------------------------------------------

# @topic category property_types/Basic Types
# This category contains all basic types, in particular those that wrap C++, GMP or perl types such as [[Int]], [[Integer]], [[Rational]], [[Long]], [[Float]], [[Array]], [[String]], ...

# @topic category property_types/Artificial
# These types are auxiliary artifacts helping to build other classes,
# primarily representing template parameters or enumeration constants.
# They should not be used alone as property types or function arguments.
# In the most cases they won't even have user-accessible constructors.

# @topic category functions/Combinatorics
# This category contains combinatorial functions.

# @topic category functions/Arithmetic
# These are functions that perform arithmetic computations.

# @topic category functions/Linear Algebra
# These functions are for algebraic computations and constructions of special matrices.

# @topic category functions/Data Conversion
# This contains functions for data conversions and type casts.


# @category Basic Types
# Plain text without any imposed structure.
declare property_type Text {

   method construct() { undef }

   method construct($) { "$_[1]" }

   type_method parse { $_[1] }

   type_method equal { $_[1] eq $_[2] }
}

##################################################################################

# @category Basic Types
# Corresponds to the C++ type __std::string__.
declare property_type String : c++ (builtin => 'std::string') {

   method construct() { "" }

   method construct($) { "$_[1]" }

   type_method parse { $_[1] =~ $quoted_re ? $+{quoted} : $_[1] }

   type_method toString {
      my ($proto, $value)=@_;
      !length($value) || $value =~ /\s/ ? "'$value'" : $value
   }

   type_method equal { $_[1] eq $_[2] }

   type_method XMLdatatype { "text-pattern: \\S+|'.*'" }
}

##################################################################################

# @category Basic Types
# Corresponds to the C++ type __int__.
declare property_type Int : c++ (builtin => 'int') {

   method construct() { 0 }

   method construct($) {
      use integer;
      0+$_[1]
   }

   type_method parse { extract_integer($_[1]) }

   type_method isa { is_integer($_[1]) }

   type_method XMLdatatype { "integer" }
}

# @category Basic Types
# Corresponds to the C++ type __long__.
# This type is primarily needed for automatic generated function wrappers,
# because perl integral constants are always kept as a long.
declare property_type Long : Int : c++ (builtin => 'long');

# @category Basic Types
# Corresponds to the C++ type __bool__.
declare property_type Bool : c++ (builtin => 'bool') {

   method construct() { 0 }

   method construct($) { !(!$_[1]) }

   type_method parse {
      if ($_[1] =~ m{^\s* (?i: (\d+) | (true) | false) \s*$ }x) {
         (defined($1) ? $1!=0 : defined($2))+0
      } else {
         croak( "parse error: invalid boolean value $_[1]" );
      }
   }

   type_method isa { is_boolean($_[1]) }

   type_method toString { $_[1] ? "true" : "false" }

   type_method XMLdatatype { "boolean" }
}

# @category Basic Types
# Corresponds to the C++ type __double__.
declare property_type Float : upgrades(Int) : c++ (builtin => 'double') {

   method construct() { 0.0 }

   method construct($) { 0.0+$_[1] }

   type_method parse { extract_float($_[1]) }

   type_method equal {
      my ($proto, $a, $b)=@_;
      if (isinf($a) || isinf($b)) {
         return isinf($a) == isinf($b);
      }
      my $max=max(abs($a), abs($b));
      $max<=1e-7 || abs($a-$b)<=1e-7*$max;
   }

   type_method isa { is_float($_[1]) }

   # produce an infinitely large positive value
   user_method inf() : static : c++ (name => 'std::numeric_limits<double>::infinity');

   # produce an infinitely large negative value
   user_method minus_inf() { -&inf }

   # the XSD library type allows only INF for infinite values,
   # while perl prints them as Inf and c++ library as inf
   # fortunately, the input method is case-insensitive
   type_method XMLdatatype { ("double", "pattern: -?[Ii]nf") }
}

# @category Artificial
declare property_type LocalFloatEpsilon : c++ (special => 'pm::local_epsilon_keeper');

function local_epsilon($) : c++ (include => "polymake/internal/comparators.h");

##################################################################################

sub inhibited_num { croak( "can't use ", $_[0]->type->full_name, " in a context allowing only primitive numeric types" ) }

# @category Basic Types
# An integer of arbitrary size.
declare property_type Integer : upgrades(Int) : c++ \
   (operators => '++ -- bool neg ! +:int -:int *:int /:int +=:int -=:int *=:int /=:int %:int %=:int  \
                  << <<= >> >>= **(Integer::pow(*,*:int)) abs(abs(*)) @compare <=>(compare(*) : method)',     \
    include => "polymake/Integer.h") {

   # produce an infinitely large positive value
   user_method inf() : static : c++ (name => 'std::numeric_limits<Integer>::max');

   # produce an infinitely large negative value
   user_method minus_inf() : static : c++ (name => 'std::numeric_limits<Integer>::min');

   use overload '0+' => \&inhibited_num;

   type_method XMLdatatype { "pattern: -?(\\d+|inf)" }
}


# use automatically for all literal long integral constants in perl input
namespaces::intercept_const_creation(undef, 'I', sub { (typeof Integer)->construct->($_[0]) });

# @category Combinatorics
# Computes the binomial coefficient __//n// choose //k//__.
# Negative values of //n// (and //k//) are supported.
# @param Int n
# @param Int k
# @return Int //n// choose //k//
# @example Print 6 choose 4 like this:
# > print binomial(6,4);
# | 15
user_function binomial(*,$) : c++ (name => 'Integer::binom', include => "polymake/Integer.h");

# @category Basic Types
# A class for rational numbers.
# You can easily create a Rational like that: $x = 1/2;
declare property_type Rational : upgrades(Integer) : c++ \
   (operators => '++ -- bool neg ! +:int -:int *:int /:int +=:int -=:int *=:int /=:int  \
                  << <<= >> >>= **(Rational::pow(*,*:int)) abs(abs(*)) @compare <=>(compare(*) : method)',         \
    include => "polymake/Rational.h") {

   # Literal fraction constants in the perl input are also built with this constructor.
   method construct(*,*) : c++ : const_creation( / );

   # Produce an infinitely large positive value.
   user_method inf() : static : c++ (name => 'std::numeric_limits<Rational>::max');

   # Produce an infinitely large negative value.
   user_method minus_inf() : static : c++ (name => 'std::numeric_limits<Rational>::min');

   use overload '0+' => \&inhibited_num;

   type_method XMLdatatype { "pattern: -?(\\d+(/\\d+)?|inf)" }
}

# @category Basic Types
# A wrapper for AccurateFloat.
declare property_type AccurateFloat : c++ \
   (operators => 'bool neg ! +:int -:int *:int /:int +=:int -=:int *=:int /=:int  \
                  abs(abs(*)) @compare <=>(compare(*) : method)',         \
    include => "polymake/AccurateFloat.h") {

#   method construct(*) : c++ : const_creation( );

}  

# @category Arithmetic
# Returns the __numerator__ of //a// in a reduced representation.
# @param Rational a
# @return Integer
user_function numerator(Rational:lvalue_opt:anchor) : c++;


# @category Arithmetic
# Returns the __denominator__ of //a// in a reduced representation.
# @param Rational a
# @return Integer
user_function denominator(Rational:lvalue_opt:anchor) : c++;


# @category Arithmetic
# The __floor function__. Returns the smallest integral number not larger than //a//.
# @param Rational a
# @return Rational
# @example
# > print floor(1.8);
# | 1

user_function floor(*) : c++ (include => "polymake/Rational.h");


# @category Arithmetic
# The __ceiling function__. Returns the smallest integral number not smaller than //a//.
# @param Rational a
# @return Rational
user_function ceil(*) : c++ (include => "polymake/Rational.h");


# @category Arithmetic
# Check whether the given number has a finite value.
# @param SCALAR a
# @return Bool
# @example
# > print isfinite('inf');
# | 
# > print isfinite(23);
# | 1

user_function isfinite(*) : c++ (include => "polymake/Integer.h");


# @category Arithmetic
# Check whether the given number has an infinite value.  Return -1/+1 for infinity and 0 for all finite values.
# @param SCALAR a
# @return Int
# @example
# > print isinf('inf');
# | 1
# > print isinf(23);
# | 0
user_function isinf(*) : c++ (include => "polymake/Integer.h");


# @category Arithmetic
# Computes the ratio of two given integral numbers under the assumption that the dividend is a multiple of the divisor.
# @param Integer a
# @param Integer b a divisor of //a//
# @return Integer
# @example
# > print div_exact(10,5);
# | 2
user_function div_exact(*:int, *:int) : c++ (include => "polymake/numerical_functions.h");


# @topic category property_types/Arithmetic
# These types are needed as return types of arithmetic computations.

# @category Arithmetic
# The complete result of an integral division, [[quot|quotient]] and [[rem|remainder]].
# @tparam Scalar
declare property_type Div<Scalar> : c++ (include => "polymake/numerical_functions.h");

# @topic property_types/Arithmetic/Div/methods/quot
# The __quotient__.
# @return Scalar

# @topic property_types/Arithmetic/Div/methods/rem
# The __remainder__.
# @return Scalar

# @category Arithmetic
# Compute the quotient and remainder of //a// and //b// in one operation.
# @param Int a
# @param Int b
# @return Div
# @example
# > $d = div(10,3);
# > print $d->quot;
# | 3
# > print $d->rem;
# | 1

user_function div(*,*) : c++ (include => "polymake/numerical_functions.h");


# @category Arithmetic
# Computes the __greatest common divisor__ of two integers.
# @param Int a
# @param Int b
# @return Int
# @example
# > print gcd(6,9);
# | 3
user_function gcd(*:int, *:int) : c++ (include => "polymake/numerical_functions.h");

# @category Arithmetic
# The complete result of the calculation of the __greatest common divisor__ of two numbers //a// and //b//:
#	[[g]]=gcd(a,b)
#	[[g]]=a*[[p]]+b*[[q]]
#	a=[[g]]*[[k1]]
#	b=[[g]]*[[k2]]
declare property_type ExtGCD<Scalar> : c++ (include => "polymake/numerical_functions.h");

# @topic property_types/Arithmetic/ExtGCD/methods/g
# The __greatest common divisor__ of //a// and //b//.
# @return Int

# @topic property_types/Arithmetic/ExtGCD/methods/p
# The __co-factor__ of //a//.
# @return Int

# @topic property_types/Arithmetic/ExtGCD/methods/q
# The __co-factor__ of //b//.
# @return Int

# @topic property_types/Arithmetic/ExtGCD/methods/k1
# The __factor__ of //a//.
# @return Int

# @topic property_types/Arithmetic/ExtGCD/methods/k2
# The __factor__ of //b//.
# @return Int


# @category Arithmetic
# Compute the greatest common divisor of two numbers (a,b) and accompanying co-factors.
# @param Int a
# @param Int b
# @return ExtGCD
# @example
# > $GCD = ext_gcd(15,6);
# The GCD of the numbers can then be accessed like this:
# > print $GCD->g;
# | 3
# The ExtGCD type also stores the Bezout coefficients (thus integers p and q such that g=a*p+b*q)...
# > print $GCD->p;
# | 1
# print $GCD->q;
# | -2
# ...and the quotients k1 of a and k2 of b by g.
# > print $GCD->k1;
# | 5
# > print $GCD->k2;
# | 2

user_function ext_gcd(*:int, *:int) : c++ (include => "polymake/numerical_functions.h");


# @category Arithmetic
# Computes the __least common multiple__ of two integers.
# @param Int a
# @param Int b
# @return Int
# @example
# > print lcm(6,9);
# | 18

user_function lcm(*:int, *:int) : c++ (include => "polymake/numerical_functions.h");


# @category Arithmetic
# Computes the factorial //n//! = n&middot;(n-1)&middot;(n-2)&middot;...&middot;2&middot;1.
# @param Int n >=0
# @return Integer //n//!
user_function fac(*:int) : c++ (name => 'Integer::fac', include => "polymake/Integer.h");


# @category Internal
# type checking
function is_ordered_field_with_unlimited_precision($) {
   my $type=shift;
   is_object($type) && $type->abstract or defined(wantarray) ? 0 : croak( $type->full_name, " is not a suitable coordinate type" )
}

function is_ordered_field_with_unlimited_precision(Rational) { 1 }

# @category Internal
# type checking
function is_ordered_field($) { &is_ordered_field_with_unlimited_precision }

function is_ordered_field(Float) { 1 }


# @category Arithmetic
# Compare with the zero (0) value of the corresponding data type.
# @param SCALAR s
# @return Bool
user_function is_zero(*) : c++ (include => "polymake/internal/comparators.h");

# @category Arithmetic
# Compare with the one (1) value of the corresponding data type.
# @param SCALAR s
# @return Bool
user_function is_one(*) : c++ (include => "polymake/internal/comparators.h");


# Explicit conversion to different scalar type.
# @tparam Target
# @param SCALAR s
# @return Target
user_function convert_to<Target>($) {
   my $target_type=typeof Target;
   if ($target_type->isa->($_[0])) {
      $_[0]
   } else {
      $target_type->construct->($_[0]);
   }
}

##################################################################################

# @category Basic Types
# Realizes quadratic extensions of fields.
# 
# You can construct the value a+b\(\sqrt r\) via QuadraticExtension(a, b, r) (where a, b, r are of type //Field//).
# @tparam Field default: [[Rational]]
declare property_type QuadraticExtension<Field=Rational> : upgrades(Field) : c++ \
   (operators => '! @arith @compare abs(abs(*))', include => "polymake/QuadraticExtension.h") {

   # Construct q=a+b*sqrt(r).
   # @param Field a
   # @param Field b
   # @param Field r
   method construct(*, *, *) : c++;

   # construct q=a
   # @param Field a
   method construct(type_upgrades_to<Field>) : c++;

   use overload '0+' => \&inhibited_num;
}

function conjugate(QuadraticExtension) : c++;

function is_ordered_field_with_unlimited_precision(QuadraticExtension) { 1 }


##################################################################################

# @category Artificial
# A perl scalar of whatever contents.
declare property_type SCALAR : c++ (special => 'perl::Scalar');

# @category Artificial
# A perl array of whatever contents.
declare property_type ARRAY : c++ (special => 'perl::Array');

##################################################################################

*is_container=\&Core::CPlusPlus::is_container;

# @category Basic Types
# Corresponds to the C++ type __std::list__.
# @tparam Element
declare property_type List<Element> : c++ (name => 'std::list', include => "polymake/list", operators => '@compare'){

   # Return size of the list in constant time.
   # @return Int
   method size() : c++;

}

##################################################################################

# primarily used in test suites
function compare_float_sequences($$) {
   my ($seq1, $seq2)=@_;
   my $l=$#$seq1;
   if ($l == $#$seq2) {
      my $fp=typeof Float;
      my $i=0;
      foreach my $e1 (@$seq1) {
         $fp->equal->($e1, $seq2->[$i++]) or return 0;
      }
      1
   }
}

# @category Basic Types
# An array with elements of type //Element//.
# Corresponds to the C++ type [[Array]].
# @tparam Element
declare property_type Array<Element> : (instanceof Polymake::Core::ObjectType(Element) ? Polymake::Core::BigObjectArray : undef) \
   : c++ (include => "polymake/Array.h", operators => '@eq' ) {

   # Returns the size.
   # @return Int
   user_method size { scalar @{$_[0]} }

   type_method init {
      my ($proto)=@_;
      if ($proto->super == typeof Polymake::Core::BigObjectArray) {
         $proto->super->init_constructor($proto);
      } elsif ($proto->params->[0] == typeof Float) {
         $proto->equal=\&compare_float_sequences;
      }
   }
}

##################################################################################

# @category Artificial
# An iterator over a sequence of objects.
# The objects may be stored in a container or be generated on the fly.
# @tparam Element
declare property_type Iterator<*> : c++ {

   # inherit the overloaded ++, bool, and deref
   @ISA=qw( Polymake::Core::CPlusPlus::Iterator );

   # The index of the current element.
   # If the iterator does not support indexing, returns undef.
   # @return Int
   method index() : c++;
}

# Create an iterator visiting all elements in a container implemented as a C++ object.
function entire(*:anchor) : c++ : returns(Iterator);

# @category Artificial
# One-dimensional collection of objects, aka container in STL
# It can be accessed like a perl array.  Sequential iteration via foreach() is always supported;
# random access might be supported depending on the underlying C++ object.
declare property_type Container<*> : c++ {

   # The number of elements in the container
   # Note that for a sparse container, this number can be less or equal to scalar(@$container)
   # because the latter includes all implicit zeros and/or deleted elements
   method size() : c++;
}

##################################################################################

# @category Basic Types
# Corresponds to the C++ type __std::pair__.
# @tparam First
# @tparam Second
declare property_type Pair<First,Second> \
   : c++ (name => 'std::pair', include => "polymake/client.h", fields => [qw(first second)], operators => '@compare');

##################################################################################

# @category Artificial
# @tparam X
declare property_type Serialized<X> : c++ (name => "pm::Serialized", include => "polymake/client.h");

##################################################################################

# backward compatibility, see issue #864
sub deprecated_elem_access {
   my $obj=shift;
   (undef, my ($file, $line))=caller;
   if ($file eq "input") {
      warn_print("Matrix element access via function call operator is deprecated.\nPlease use method `elem' instead: \$Matrix->elem(\$i,\$j)");
   } else {
      state %told;
      $told{"$file#$line"} //=
        warn_print("Deprecated matrix element access operator still in use at $file, line $line.\nPlease replace with method `elem': \$Matrix->elem(\$i,\$j)");
   }
   use namespaces::AnonLvalue 1 ();
   sub { $obj->elem(@_) };
}

INCLUDE
  set_types
  algebraic_types
  polynomial_types
  graph_types


# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End:
