Skip to content

Commit 555bee7

Browse files
committed
2 parents 64528df + 654d09c commit 555bee7

18 files changed

+257
-94
lines changed

build/pkgs/4ti2/distros/arch.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
4ti2

build/pkgs/4ti2/distros/cygwin.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib4ti2-devel

build/pkgs/4ti2/distros/debian.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
4ti2

build/pkgs/4ti2/distros/fedora.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
4ti2

build/pkgs/4ti2/distros/freebsd.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
math/4ti2

build/pkgs/4ti2/distros/gentoo.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sci-mathematics/4ti2

build/pkgs/4ti2/spkg-configure.m4

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
SAGE_SPKG_CONFIGURE([4ti2], [
2+
SAGE_SPKG_DEPCHECK([gmp mpir glpk zlib], [
3+
dnl Debian installs these programs with an executable prefix "4ti2-",
4+
dnl OpenSUSE uses the prefix "4ti2_".
5+
dnl Singular checks for unprefixed and prefixed with "4ti2-".
6+
dnl Polymake does not check for prefixed binaries.
7+
m4_foreach([prog], [hilbert,markov,graver,zsolve,qsolve,rays,ppi,circuits,groebner], [
8+
AC_CHECK_PROGS([FOURTITWO_]m4_toupper(prog), prog [4ti2-]prog [4ti2_]prog)
9+
AS_VAR_IF([FOURTITWO_]m4_toupper(prog), [""], [sage_spkg_install_4ti2=yes])
10+
AC_SUBST([FOURTITWO_]m4_toupper(prog))
11+
])
12+
dnl Adapted from https://github.com/latte-int/latte/blob/master/m4/4ti2-check.m4
13+
AC_MSG_CHECKING(for library 4ti2gmp)
14+
BACKUP_CXXFLAGS=${CXXFLAGS}
15+
BACKUP_LIBS=${LIBS}
16+
FORTYTWO_CXXFLAGS="-D__STDC_LIMIT_MACROS -D_4ti2_GMP_"
17+
FORTYTWO_LIBS="-l4ti2gmp -lzsolve"
18+
CXXFLAGS="${BACKUP_CXXFLAGS} ${FORTYTWO_CXXFLAGS} ${GMP_CFLAGS}"
19+
LIBS="${BACKUP_LIBS} ${FORTYTWO_LIBS} ${GMP_LIBS}"
20+
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
21+
#include "4ti2/4ti2.h"
22+
]], [[ _4ti2_rays_create_state(_4ti2_PREC_INT_ARB);
23+
]])],[
24+
AC_MSG_RESULT([yes])
25+
],[
26+
AC_MSG_RESULT([no])
27+
sage_spkg_install_4ti2=yes
28+
])
29+
CXXFLAGS=${BACKUP_CXXFLAGS}
30+
LIBS=${BACKUP_LIBS}
31+
])
32+
])

pkgs/sage-conf/sage_conf.py.in

+11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace('${prefix}', SAGE_LOCAL)
2727

2828
SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@"
2929

30+
# Names or paths of the 4ti2 executables
31+
FOURTITWO_HILBERT = "@FOURTITWO_HILBERT@"
32+
FOURTITWO_MARKOV = "@FOURTITWO_MARKOV@"
33+
FOURTITWO_GRAVER = "@FOURTITWO_GRAVER@"
34+
FOURTITWO_ZSOLVE = "@FOURTITWO_ZSOLVE@"
35+
FOURTITWO_QSOLVE = "@FOURTITWO_QSOLVE@"
36+
FOURTITWO_RAYS = "@FOURTITWO_RAYS@"
37+
FOURTITWO_PPI = "@FOURTITWO_PPI@"
38+
FOURTITWO_CIRCUITS = "@FOURTITWO_CIRCUITS@"
39+
FOURTITWO_GROEBNER = "@FOURTITWO_GROEBNER@"
40+
3041
# Colon-separated list of pkg-config modules to search for cblas functionality.
3142
# We hard-code it here as cblas because configure (build/pkgs/openblas/spkg-configure.m4)
3243
# always provides cblas.pc, if necessary by creating a facade pc file for a system BLAS.

src/sage/doctest/control.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,8 @@ def __init__(self, options, args):
407407
from sage.features import package_systems
408408
options.optional.update(system.name for system in package_systems())
409409

410-
from sage.features.sagemath import sage_optional_tags
411-
options.optional.update(sage_optional_tags())
410+
from sage.features.sagemath import sage_features
411+
options.optional.update(feature.name for feature in sage_features())
412412

413413
# Check that all tags are valid
414414
for o in options.optional:

src/sage/doctest/external.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,19 @@ def has_rubiks():
304304
from sage.features.rubiks import Rubiks
305305
return Rubiks().is_present()
306306

307+
def has_4ti2():
308+
"""
309+
Test if the 4ti2 package is available.
310+
311+
EXAMPLES::
312+
313+
sage: from sage.doctest.external import has_4ti2
314+
sage: has_4ti2() # optional -- 4ti2
315+
FeatureTestResult('4ti2', True)
316+
"""
317+
from sage.features.four_ti_2 import FourTi2
318+
return FourTi2().is_present()
319+
307320
def external_software():
308321
"""
309322
Return the alphabetical list of external software supported by this module.
@@ -346,7 +359,8 @@ class AvailableSoftware(object):
346359
347360
sage: from sage.doctest.external import external_software, available_software
348361
sage: external_software
349-
['cplex',
362+
['4ti2',
363+
'cplex',
350364
'ffmpeg',
351365
'graphviz',
352366
'gurobi',

src/sage/doctest/reporting.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def have_optional_tag(self, tag):
141141
False
142142
143143
"""
144-
if tag in self.controller.options.optional:
144+
if self.controller.options.optional is True or tag in self.controller.options.optional:
145145
return True
146146
if 'external' in self.controller.options.optional:
147147
if tag in available_software.seen():

src/sage/env.py

+9
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
211211
MAXIMA_FAS = var("MAXIMA_FAS")
212212
KENZO_FAS = var("KENZO_FAS")
213213
SAGE_NAUTY_BINS_PREFIX = var("SAGE_NAUTY_BINS_PREFIX", "")
214+
FOURTITWO_HILBERT = var("FOURTITWO_HILBERT")
215+
FOURTITWO_MARKOV = var("FOURTITWO_MARKOV")
216+
FOURTITWO_GRAVER = var("FOURTITWO_GRAVER")
217+
FOURTITWO_ZSOLVE = var("FOURTITWO_ZSOLVE")
218+
FOURTITWO_QSOLVE = var("FOURTITWO_QSOLVE")
219+
FOURTITWO_RAYS = var("FOURTITWO_RAYS")
220+
FOURTITWO_PPI = var("FOURTITWO_PPI")
221+
FOURTITWO_CIRCUITS = var("FOURTITWO_CIRCUITS")
222+
FOURTITWO_GROEBNER = var("FOURTITWO_GROEBNER")
214223
ARB_LIBRARY = var("ARB_LIBRARY", "arb")
215224
CBLAS_PC_MODULES = var("CBLAS_PC_MODULES", "cblas:openblas:blas")
216225
ECL_CONFIG = var("ECL_CONFIG", "ecl-config")

src/sage/features/four_ti_2.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from . import Executable
2+
from .join_feature import JoinFeature
3+
4+
5+
class FourTi2Executable(Executable):
6+
r"""
7+
Feature for the 4ti2 executables.
8+
"""
9+
def __init__(self, name):
10+
from sage.env import SAGE_ENV
11+
Executable.__init__(self,
12+
name="4ti2-" + name,
13+
executable=SAGE_ENV.get("FOURTITWO_" + name.upper(), None) or name,
14+
spkg="4ti2")
15+
16+
17+
class FourTi2(JoinFeature):
18+
r"""
19+
A :class:`sage.features.Feature` describing the presence of the ``4ti2`` executables.
20+
21+
EXAMPLES::
22+
23+
sage: from sage.features.four_ti_2 import FourTi2
24+
sage: FourTi2().is_present() # optional - 4ti2
25+
FeatureTestResult('4ti2', True)
26+
"""
27+
def __init__(self):
28+
JoinFeature.__init__(self, '4ti2',
29+
[FourTi2Executable(x)
30+
# same list is tested in build/pkgs/4ti2/spkg-configure.m4
31+
for x in ('hilbert', 'markov', 'graver', 'zsolve', 'qsolve',
32+
'rays', 'ppi', 'circuits', 'groebner')])

src/sage/features/join_feature.py

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
r"""
2+
Join features
3+
"""
4+
5+
from . import Feature, FeatureTestResult
6+
7+
8+
class JoinFeature(Feature):
9+
r"""
10+
Join of several :class:`sage.features.Feature` instances.
11+
12+
EXAMPLES::
13+
14+
sage: from sage.features import Executable
15+
sage: from sage.features.join_feature import JoinFeature
16+
sage: F = JoinFeature("shell-boolean",
17+
....: (Executable('shell-true', 'true'),
18+
....: Executable('shell-false', 'false')))
19+
sage: F.is_present()
20+
FeatureTestResult('shell-boolean', True)
21+
sage: F = JoinFeature("asdfghjkl",
22+
....: (Executable('shell-true', 'true'),
23+
....: Executable('xxyyyy', 'xxyyyy-does-not-exist')))
24+
sage: F.is_present()
25+
FeatureTestResult('xxyyyy', False)
26+
"""
27+
def __init__(self, name, features, spkg=None, url=None):
28+
"""
29+
TESTS:
30+
31+
The empty join feature is present::
32+
33+
sage: from sage.features.join_feature import JoinFeature
34+
sage: JoinFeature("empty", ()).is_present()
35+
FeatureTestResult('empty', True)
36+
"""
37+
if spkg is None:
38+
spkgs = set(f.spkg for f in features if f.spkg)
39+
if len(spkgs) > 1:
40+
raise ValueError('given features have more than one spkg; provide spkg argument')
41+
elif len(spkgs) == 1:
42+
spkg = next(iter(spkgs))
43+
if url is None:
44+
urls = set(f.url for f in features if f.url)
45+
if len(urls) > 1:
46+
raise ValueError('given features have more than one url; provide url argument')
47+
elif len(urls) == 1:
48+
url = next(iter(urls))
49+
super().__init__(name, spkg=spkg, url=url)
50+
self._features = features
51+
52+
def _is_present(self):
53+
r"""
54+
Test for the presence of the join feature.
55+
56+
EXAMPLES::
57+
58+
sage: from sage.features.latte import Latte
59+
sage: Latte()._is_present() # optional - latte_int
60+
FeatureTestResult('LattE', True)
61+
"""
62+
for f in self._features:
63+
test = f._is_present()
64+
if not test:
65+
return test
66+
return FeatureTestResult(self, True)
67+
68+
def is_functional(self):
69+
r"""
70+
Test whether the join feature is functional.
71+
72+
EXAMPLES::
73+
74+
sage: from sage.features.latte import Latte
75+
sage: Latte().is_functional() # optional - latte_int
76+
FeatureTestResult('LattE', True)
77+
"""
78+
for f in self._features:
79+
test = f.is_functional()
80+
if not test:
81+
return test
82+
return FeatureTestResult(self, True)

src/sage/features/latte.py

+5-37
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Check for LattE
44
"""
55
from . import Executable, Feature, FeatureTestResult
6+
from .join_feature import JoinFeature
7+
68

79
LATTE_URL = "https://www.math.ucdavis.edu/~latte/software.php"
810

@@ -27,7 +29,7 @@ def __init__(self):
2729
url=LATTE_URL)
2830

2931

30-
class Latte(Feature):
32+
class Latte(JoinFeature):
3133
r"""
3234
A :class:`sage.features.Feature` describing the presence of the ``LattE``
3335
binaries which comes as a part of ``latte_int``.
@@ -46,39 +48,5 @@ def __init__(self):
4648
sage: isinstance(Latte(), Latte)
4749
True
4850
"""
49-
Feature.__init__(self, "LattE")
50-
51-
def _is_present(self):
52-
r"""
53-
Test for the presence of LattE binaries.
54-
55-
EXAMPLES::
56-
57-
sage: from sage.features.latte import Latte
58-
sage: Latte()._is_present() # optional - latte_int
59-
FeatureTestResult('LattE', True)
60-
"""
61-
62-
test = (Latte_count()._is_present() and
63-
Latte_integrate()._is_present())
64-
if not test:
65-
return test
66-
67-
return FeatureTestResult(self, True)
68-
69-
def is_functional(self):
70-
r"""
71-
Test whether count and integrate are functionals.
72-
73-
EXAMPLES::
74-
75-
sage: from sage.features.latte import Latte
76-
sage: Latte().is_functional() # optional - latte_int
77-
FeatureTestResult('LattE', True)
78-
"""
79-
test = (Latte_count().is_functional() and
80-
Latte_integrate().is_functional())
81-
if not test:
82-
return test
83-
84-
return FeatureTestResult(self, True)
51+
JoinFeature.__init__(self, "LattE",
52+
(Latte_count(), Latte_integrate()))

0 commit comments

Comments
 (0)