1#!/usr/local/bin/python3.8
2import os
3import sys
4import logging
5
6import re
7
8# THIS PART MUST BE EXACTLY IDENTICAL IN cado-nfs.py and cado-nfs-client.py
9
10# Three possible locations for this script
11#
12# The installed path. Then we should rely on the @-escaped paths, and
13# determine the location of all our utility stuff based on that.
14#
15# The build directory. We rely on the presence of a magic file called
16# source-location.txt, and we fetch the python source code from there
17#
18# The source tree. We call the ./scripts/build_environment.sh script to
19# determine where the binaries are being put.
20
21import subprocess
22import locale
23
24pathdict=dict()
25
26one_pyfile_example_subpath = "scripts/cadofactor/workunit.py"
27
28def detect_installed_tree(pathdict):
29    mydir = os.path.normpath(os.path.dirname(sys.argv[0]))
30    md = mydir.split(os.path.sep)
31    install_tree = mydir
32    bd = "@BINSUFFIX@".split(os.path.sep)
33    while md and bd and md[-1] == bd[-1]:
34        md.pop()
35        bd.pop()
36        install_tree = os.path.normpath(os.path.join(install_tree, ".."))
37    if bd:
38        if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
39            print("{} does not end in @BINSUFFIX@".format(mydir))
40        return False
41    example = os.path.join(install_tree, "@LIBSUFFIX@", one_pyfile_example_subpath)
42    t = os.path.exists(example)
43    if not t:
44        if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
45            print("{} does not exist".format(example))
46        return False
47
48    # make all this relocatable, it doesn't cost us much.
49    # (note though that the rpaths in the binaries are likely to still
50    # contain absolute paths)
51    pathdict["pylib"] = os.path.join(install_tree, "@LIBSUFFIX@/scripts/cadofactor")
52    pathdict["data"]  = os.path.join(install_tree, "@DATASUFFIX@")
53    pathdict["lib"]   = os.path.join(install_tree, "@LIBSUFFIX@")
54    pathdict["bin"]   = os.path.join(install_tree, "@BINSUFFIX@")
55
56    if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
57        print("cado-nfs running in installed tree")
58    return True
59
60def detect_build_tree(pathdict):
61    # source-location.txt is created by our build system, and can be used
62    # *ONLY* when we call this script from the build directory. We don't
63    # want this to perspire in any installed file, of course.
64    mydir = os.path.normpath(os.path.dirname(sys.argv[0]))
65    source_location_subpath = "source-location.txt"
66    source_location_file = os.path.join(mydir, source_location_subpath)
67    if not os.path.exists(source_location_file):
68        if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
69            print("{} does not exist".format(source_location_file))
70        return False
71
72    # ok, we're in the build tree, apparently
73    source_tree = open(source_location_file, "r").read().strip()
74
75    pathdict["pylib"] = os.path.join(source_tree, "scripts/cadofactor")
76    pathdict["data"] = os.path.join(source_tree, "parameters")
77    pathdict["lib"] = mydir
78    pathdict["bin"] = mydir
79
80    if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
81        print("cado-nfs running in build tree")
82    return True
83
84def detect_source_tree(pathdict):
85    mydir = os.path.normpath(os.path.dirname(sys.argv[0]))
86    t = os.path.exists(os.path.join(mydir, one_pyfile_example_subpath))
87    helper = os.path.join(mydir, "scripts/build_environment.sh")
88    if not os.path.exists(helper):
89        if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
90            print("{} does not exist".format(helper))
91        return False
92    pipe = subprocess.Popen([helper, "--show"], stdout=subprocess.PIPE)
93    loc = locale.getdefaultlocale()[1]
94    if not loc:
95        loc="ascii"
96    output = pipe.communicate()[0].decode(loc)
97    cado_bin_path = [x.split("=",2)[1] for x in output.split("\n") if re.match("^build_tree",x)][0]
98    cado_bin_path = re.sub("^\"(.*)\"$", "\\1", cado_bin_path)
99
100    pathdict["pylib"] = os.path.join(mydir, "scripts/cadofactor")
101    pathdict["data"] = os.path.join(mydir, "parameters")
102    pathdict["lib"] = cado_bin_path
103    pathdict["bin"] = cado_bin_path
104
105    if os.environ.get("CADO_NFS_DEBUG_PATHDETECT"):
106        print("cado-nfs running in source tree")
107    return True
108
109if detect_installed_tree(pathdict):
110    pass
111elif detect_build_tree(pathdict):
112    pass
113elif detect_source_tree(pathdict):
114    pass
115else:
116    raise RuntimeError("We're unable to determine the location of the cado-nfs binaries and python files")
117
118sys.path.append(pathdict["pylib"])
119
120# END OF THE PART THAT MUST BE EXACTLY IDENTICAL IN cado-nfs.py and cado-nfs-client.py
121
122
123import cadotask
124import cadologger
125import toplevel
126import itertools
127import wudb
128from cadocommand import shellquote
129
130if __name__ == '__main__':
131    # Parse command line arguments
132
133    # Some command-line arguments are really parsed only here, while some
134    # others are relevant to the whole hierarchy of cado-nfs programs.
135    # The (hairy) logic which is used to form the definitive list of
136    # parameters (in the cadoparams sense) from what we got here on the
137    # command line is grouped in the Cado_NFS_toplevel class, down in
138    # scripts/cadofactor/toplevel.py
139
140    toplevel_params = toplevel.Cado_NFS_toplevel()
141    for key, value in pathdict.items():
142        toplevel_params.setpath(key, value)
143    logger = toplevel_params.logger
144    parameters, db = toplevel_params.get_cooked_parameters()
145
146    # We have to remove the credential info from the database which gets
147    # stored in the parameter snapshot. We must also make sure that we
148    # store this info always at the root of the param tree, or we may end
149    # up do bizarre things with duplicated keys if we resume from a
150    # parameter snapshot file *and* we have something on the command
151    # line.
152    if parameters.get_or_set_default("database", None):
153        parameters.replace("database", db.uri_without_credentials)
154        # do this so that the parameter does not appear unused.
155        parameters.get_or_set_default("database")
156
157
158    # well, this *must* exist, right ?
159    name = parameters.get_or_set_default("tasks.name")
160    wdir = parameters.get_or_set_default("tasks.workdir")
161
162    # Add a logger to capture the command lines of programs we run
163    cmdfilename = os.path.join(wdir, name + ".cmd")
164    logger.addHandler(cadologger.CmdFileHandler(cmdfilename))
165
166    # Add a logger to write debugging information to a log file
167    filelvl = getattr(cadologger, toplevel_params.args.filelog.upper())
168    logfilename = os.path.join(wdir, name + ".log")
169    filehandler = cadologger.FileHandler(filename = logfilename, lvl = filelvl)
170    logger.addHandler(filehandler)
171
172
173    cmdline=" ".join([shellquote(arg, idx == 0) for idx, arg in enumerate(sys.argv)])
174    cmdline=cmdline.replace(db.uri, db.uri_without_credentials)
175    logger.info("Command line parameters: %s", cmdline)
176
177
178    logger.debug("Root parameter dictionary:\n%s", parameters)
179
180
181    # Write a snapshot of the parameters to a file
182    for counter in itertools.count():
183        snapshot_basename = name + ".parameters_snapshot.%d" % counter
184        snapshot_filename = os.path.join(wdir, snapshot_basename)
185        if not os.path.isfile(snapshot_filename):
186            break
187    with open(snapshot_filename, "w") as snapshot_file:
188        logger.debug("Writing parameter snapshot to %s", snapshot_filename)
189        snapshot_file.write(str(parameters))
190        snapshot_file.write("\n")
191
192    logger.info("If this computation gets interrupted, it can be resumed with %s %s", sys.argv[0], snapshot_filename)
193
194    factorjob = cadotask.CompleteFactorization(db=db,
195                                               parameters = parameters,
196                                               path_prefix = [])
197
198    if toplevel_params.args.verboseparam:
199        logger.info("Summary of all recognized parameters\n" +
200                factorjob.parameter_help)
201
202    factors = factorjob.run()
203
204    dlp_param = parameters.myparams({"dlp": False,}, "")
205    dlp = dlp_param["dlp"]
206    checkdlp_param = parameters.myparams({"checkdlp": True ,}, "")
207    checkdlp = checkdlp_param["checkdlp"]
208    target_param = parameters.myparams({"target": "",}, "")
209    target = target_param["target"]
210    if factors is None:
211        toplevel_params.purge_temp_files(nopurge=True)
212        sys.exit("Error occurred, terminating")
213    else:
214        toplevel_params.purge_temp_files()
215
216    if not dlp:
217        print(" ".join(factors))
218    else:
219        if checkdlp:
220            p = int(factors[0])
221            ell = int(factors[1])
222            log2 = int(factors[2])
223            log3 = int(factors[3])
224            logger.info("Checking that log(2) and log(3) are consistent...")
225            logger.info("  p = " + str(p))
226            logger.info("  ell = " + str(ell))
227            logger.info("  log2 = " + str(log2))
228            logger.info("  log3 = " + str(log3))
229            assert (p-1) % ell == 0
230            assert pow(3, log2*((p-1) // ell), p) == pow(2, log3*((p-1) // ell), p)
231            if target != "":
232                logtarget = int(factors[4])
233                logger.info("Also check log(target) vs log(2) ...")
234                assert pow(int(target), log2*((p-1) // ell), p) == pow(2, logtarget*((p-1) // ell), p)
235        else:
236            logger.info("No check was performed. Logarithms of the factor base elements are in %s" % factorjob.request_map[cadotask.Request.GET_DLOG_FILENAME]())
237        if target != "":
238            logtarget = int(factors[4])
239            logger.info("target = " + str(target))
240            logger.info("log(target) = " + str(logtarget))
241            print(str(logtarget))
242        logger.info("If you want to compute a new target, run %s %s target=<target>", sys.argv[0], snapshot_filename)
243
244