#  Copyright (c) 1997-2015
#  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] }
}

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

# @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]) }
}

# @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" }
}

# @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)=@_;
      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 }
}

# @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;
}


# 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//
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;
}


# @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 Integer a
# @return Bool
# @example > print isfinite('inf');
# | 0
# > 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 Integer a
# @return Bool
# @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 Int a
# @param Int b a divisor of //a//
# @return Int
# @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', 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++;
}

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');

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

# 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;
      }
   }
}

function permuted(Array, *) : c++ (include => "polymake/permutations.h") : builtin_sub {
   my ($array, $perm)=@_;
   local $Core::PropertyType::trusted_value=1;
   $array->type->construct->([ @$array[ @$perm ] ]);
}

function permuted_inv(Array, *) : c++ (include => "polymake/permutations.h") : builtin_sub {
   my ($array, $perm)=@_;
   my @inv; $#inv=$#$array;
   $inv[$perm->[$_]]=$array->[$_] for 0..$#inv;
   local $Core::PropertyType::trusted_value=1;
   $array->type->construct->(\@inv);
}

function permuted_elements(Array, $) {
   my ($array, $perm)=@_;
   local $Core::PropertyType::trusted_value=1;
   $array->type->construct->([ map { permuted($_,$perm) } @$array ]);
}


# @category Combinatorics
# Returns the permutation that maps //a// to //b//.
# @param Array a
# @param Array b
# @return Array<Int>
# @example > $p = find_permutation([1,8,3,4],[3,8,4,1]);
# > print $p;
# 2 1 3 0

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


# @category Combinatorics
# Returns the __cycles__ of a permutation given by //p//.
# @param Array<Int> p
# @return Array<List<Int>>
# @example > print permutation_cycles([1,0,3,2]);
# | {0 1}{2 3}

user_function permutation_cycles(*) : c++ (include => "polymake/permutations.h") : returns(@);

# @category Combinatorics
# Returns the __sign__ of the permutation given by //p//.
# @param Array<Int> p
# @return Int +1 or -1
# @example > print permutation_sign([1,0,3,2]);
# | 1

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

#FIXME: ticket 725 and 727 return type should be masqueraded Array<Array <Int> > instead of perl-Array

# @category Combinatorics
# Returns a list of all permutations of the set {0...n-1} as a perl-array
# @param Int n
# @return ARRAY
# @example To store the result in the perl array @a, type this:
# > @a = all_permutations(3);
# The array contains pointers to arrays. To access the 0-th pointer, do this:
# > $a0 = $a[0];
# To print the 0-th array itself, you have to dereference it as follows:
# > print @{ $a0 };
# | 012
# You can loop through @a by using foreach. The print statement gets the string obtained by dereferencing
# the current entry concatenated with the string " ".
# > foreach( @a ){
# > print @{ $_ }," ";
# > }
# | 012 102 201 021 120 210

user_function all_permutations($) : c++  (include => "polymake/permutations.h") : returns(@);

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

# @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 );
}

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

# @category Artificial
# Iterator over a sequence of objects having indexes.
# Examples are non-zero elements of a [[SparseVector]] or of a row/column of a [[SparseMatrix]].
declare property_type SparseIterator<*> : Iterator : c++ {

   # The index of the current element.
   # @return Int
   method index() : 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)");
   }
   my $closure=sub { $obj->elem(@_) };
   declare_lvalue($closure, 1);
   $closure;
}

INCLUDE
  set_types
  algebraic_types
  graph_types


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