~ubuntu-branches/ubuntu/vivid/slurm-llnl/vivid

« back to all changes in this revision

Viewing changes to src/common/slurm_resource_info.c

  • Committer: Bazaar Package Importer
  • Author(s): Gennaro Oliva
  • Date: 2009-09-24 23:28:15 UTC
  • mfrom: (1.1.11 upstream) (3.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20090924232815-enh65jn32q1ebg07
Tags: 2.0.5-1
* New upstream release 
* Changed dependecy from lib-mysqlclient15 to lib-mysqlclient 
* Added Default-Start for runlevel 2 and 4 and $remote_fs requirement in
  init.d scripts (Closes: #541252)
* Postinst checks for wrong runlevels 2 and 4 links
* Upgraded to standard version 3.8.3
* Add lintian overrides for missing slurm-llnl-configurator.html in doc
  base registration
* modified postrm scripts to ignore pkill return value in order to avoid
  postrm failure when no slurm process is running
* Checking for slurmctld.pid before cancelling running and pending
  jobs during package removal 

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
 *****************************************************************************
5
5
 *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
6
6
 *  Written by Susanne M. Balle, <susanne.balle@hp.com>
7
 
 *  LLNL-CODE-402394.
 
7
 *  CODE-OCEC-09-009. All rights reserved.
8
8
 *  
9
9
 *  This file is part of SLURM, a resource management program.
10
 
 *  For details, see <http://www.llnl.gov/linux/slurm/>.
 
10
 *  For details, see <https://computing.llnl.gov/linux/slurm/>.
 
11
 *  Please also read the included file: DISCLAIMER.
11
12
 *  
12
13
 *  SLURM is free software; you can redistribute it and/or modify it under
13
14
 *  the terms of the GNU General Public License as published by the Free
42
43
#  include <string.h>
43
44
#endif
44
45
 
 
46
#include <ctype.h>
45
47
#include <sys/types.h>
 
48
#include <slurm/slurm.h>
 
49
 
46
50
#include "src/common/log.h"
47
 
#include <slurm/slurm.h>
 
51
#include "src/common/slurm_protocol_api.h"
48
52
#include "src/common/slurm_resource_info.h"
 
53
#include "src/common/xmalloc.h"
 
54
#include "src/common/xstring.h"
49
55
 
50
56
#if(0)
51
57
#define DEBUG 1
52
58
#endif
53
59
 
54
60
/*
 
61
 * First clear all of the bits in "*data" which are set in "clear_mask".
 
62
 * Then set all of the bits in "*data" that are set in "set_mask".
 
63
 */
 
64
static void _clear_then_set(int *data, int clear_mask, int set_mask)
 
65
{
 
66
        *data &= ~clear_mask;
 
67
        *data |= set_mask;
 
68
}
 
69
 
 
70
/*
 
71
 * _isvalue
 
72
 * returns 1 is the argument appears to be a value, 0 otherwise
 
73
 */
 
74
static int _isvalue(char *arg) {
 
75
        if (isdigit(*arg)) {     /* decimal values and 0x... hex values */
 
76
                return 1;
 
77
        }
 
78
 
 
79
        while (isxdigit(*arg)) { /* hex values not preceded by 0x */
 
80
                arg++;
 
81
        }
 
82
        if (*arg == ',' || *arg == '\0') { /* end of field or string */
 
83
                return 1;
 
84
        }
 
85
 
 
86
        return 0;       /* not a value */
 
87
}
 
88
 
 
89
/*
55
90
 * slurm_get_avail_procs - Get the number of "available" cpus on a node
56
91
 *      given this number given the number of cpus_per_task and
57
92
 *      maximum sockets, cores, threads.  Note that the value of
72
107
 * IN alloc_cores    - Allocated cores (per socket) count to other jobs
73
108
 * IN cr_type        - Consumable Resource type
74
109
 *
75
 
 * Note: used in both the select/{linear,cons_res} plugins.
 
110
 * Note: currently only used in the select/linear plugin.
76
111
 */
77
112
int slurm_get_avail_procs(const uint16_t max_sockets,
78
113
                          const uint16_t max_cores,
281
316
 
282
317
        str[0] = '\0';
283
318
 
 
319
        if (cpu_bind_type & CPU_BIND_VERBOSE)
 
320
                strcat(str, "verbose,");
 
321
 
284
322
        if (cpu_bind_type & CPU_BIND_TO_THREADS)
285
323
                strcat(str, "threads,");
286
324
        if (cpu_bind_type & CPU_BIND_TO_CORES)
287
325
                strcat(str, "cores,");
288
326
        if (cpu_bind_type & CPU_BIND_TO_SOCKETS)
289
327
                strcat(str, "sockets,");
290
 
        if (cpu_bind_type & CPU_BIND_VERBOSE)
291
 
                strcat(str, "verbose,");
 
328
        if (cpu_bind_type & CPU_BIND_TO_LDOMS)
 
329
                strcat(str, "ldoms,");
292
330
        if (cpu_bind_type & CPU_BIND_NONE)
293
331
                strcat(str, "none,");
294
332
        if (cpu_bind_type & CPU_BIND_RANK)
295
333
                strcat(str, "rank,");
296
334
        if (cpu_bind_type & CPU_BIND_MAP)
297
 
                strcat(str, "mapcpu,");
 
335
                strcat(str, "map_cpu,");
298
336
        if (cpu_bind_type & CPU_BIND_MASK)
299
 
                strcat(str, "maskcpu,");
 
337
                strcat(str, "mask_cpu,");
 
338
        if (cpu_bind_type & CPU_BIND_LDRANK)
 
339
                strcat(str, "rank_ldom,");
 
340
        if (cpu_bind_type & CPU_BIND_LDMAP)
 
341
                strcat(str, "map_ldom,");
 
342
        if (cpu_bind_type & CPU_BIND_LDMASK)
 
343
                strcat(str, "mask_ldom,");
300
344
 
301
345
        if (*str) {
302
346
                str[strlen(str)-1] = '\0';      /* remove trailing ',' */
321
365
 
322
366
        if (mem_bind_type & MEM_BIND_VERBOSE)
323
367
                strcat(str, "verbose,");
 
368
 
324
369
        if (mem_bind_type & MEM_BIND_NONE)
325
370
                strcat(str, "none,");
326
371
        if (mem_bind_type & MEM_BIND_RANK)
328
373
        if (mem_bind_type & MEM_BIND_LOCAL)
329
374
                strcat(str, "local,");
330
375
        if (mem_bind_type & MEM_BIND_MAP)
331
 
                strcat(str, "mapmem,");
 
376
                strcat(str, "map_mem,");
332
377
        if (mem_bind_type & MEM_BIND_MASK)
333
 
                strcat(str, "maskmem,");
 
378
                strcat(str, "mask_mem,");
334
379
 
335
380
        if (*str) {
336
381
                str[strlen(str)-1] = '\0';      /* remove trailing ',' */
338
383
                strcat(str, "(null type)");     /* no bits set */
339
384
        }
340
385
}
 
386
 
 
387
void slurm_print_cpu_bind_help(void)
 
388
{
 
389
        printf(
 
390
"CPU bind options:\n"
 
391
"    --cpu_bind=         Bind tasks to CPUs\n"
 
392
"        q[uiet]         quietly bind before task runs (default)\n"
 
393
"        v[erbose]       verbosely report binding before task runs\n"
 
394
"        no[ne]          don't bind tasks to CPUs (default)\n"
 
395
"        rank            bind by task rank\n"
 
396
"        map_cpu:<list>  specify a CPU ID binding for each task\n"
 
397
"                        where <list> is <cpuid1>,<cpuid2>,...<cpuidN>\n"
 
398
"        mask_cpu:<list> specify a CPU ID binding mask for each task\n"
 
399
"                        where <list> is <mask1>,<mask2>,...<maskN>\n"
 
400
"        rank_ldom       bind task by rank to CPUs in a NUMA locality domain\n"
 
401
"        map_ldom:<list> specify a NUMA locality domain ID for each task\n"
 
402
"                        where <list> is <ldom1>,<ldom2>,...<ldomN>\n"
 
403
"        mask_ldom:<list>specify a NUMA locality domain ID mask for each task\n"
 
404
"                        where <list> is <mask1>,<mask2>,...<maskN>\n"
 
405
"        sockets         auto-generated masks bind to sockets\n"
 
406
"        cores           auto-generated masks bind to cores\n"
 
407
"        threads         auto-generated masks bind to threads\n"
 
408
"        ldoms           auto-generated masks bind to NUMA locality domains\n"
 
409
"        help            show this help message\n");
 
410
}
 
411
 
 
412
/*
 
413
 * verify cpu_bind arguments
 
414
 *
 
415
 * we support different launch policy names
 
416
 * we also allow a verbose setting to be specified
 
417
 *     --cpu_bind=threads
 
418
 *     --cpu_bind=cores
 
419
 *     --cpu_bind=sockets
 
420
 *     --cpu_bind=v
 
421
 *     --cpu_bind=rank,v
 
422
 *     --cpu_bind=rank
 
423
 *     --cpu_bind={MAP_CPU|MASK_CPU}:0,1,2,3,4
 
424
 *
 
425
 *
 
426
 * returns -1 on error, 0 otherwise
 
427
 */
 
428
int slurm_verify_cpu_bind(const char *arg, char **cpu_bind, 
 
429
                          cpu_bind_type_t *flags)
 
430
{
 
431
        char *buf, *p, *tok;
 
432
        int bind_bits =
 
433
                CPU_BIND_NONE|CPU_BIND_RANK|CPU_BIND_MAP|CPU_BIND_MASK;
 
434
        int bind_to_bits =
 
435
                CPU_BIND_TO_SOCKETS|CPU_BIND_TO_CORES|CPU_BIND_TO_THREADS;
 
436
        uint16_t task_plugin_param = slurm_get_task_plugin_param();
 
437
 
 
438
        bind_bits    |= CPU_BIND_LDRANK|CPU_BIND_LDMAP|CPU_BIND_LDMASK;
 
439
        bind_to_bits |= CPU_BIND_TO_LDOMS;
 
440
 
 
441
        if (arg == NULL) {
 
442
                if ((*flags != 0) ||            /* already set values */
 
443
                    (task_plugin_param == 0))   /* no system defaults */
 
444
                        return 0;
 
445
 
 
446
                /* set system defaults */
 
447
                xfree(*cpu_bind);
 
448
                if (task_plugin_param & CPU_BIND_NONE)
 
449
                        *flags = CPU_BIND_NONE;
 
450
                else if (task_plugin_param & CPU_BIND_TO_SOCKETS)
 
451
                        *flags = CPU_BIND_TO_SOCKETS;
 
452
                else if (task_plugin_param & CPU_BIND_TO_CORES)
 
453
                        *flags = CPU_BIND_TO_CORES;
 
454
                else if (task_plugin_param & CPU_BIND_TO_THREADS)
 
455
                        *flags |= CPU_BIND_TO_THREADS;
 
456
                else if (task_plugin_param & CPU_BIND_TO_LDOMS)
 
457
                        *flags |= CPU_BIND_TO_LDOMS;
 
458
                if (task_plugin_param & CPU_BIND_VERBOSE)
 
459
                        *flags |= CPU_BIND_VERBOSE;
 
460
                return 0;
 
461
        }
 
462
 
 
463
        /* Start with system default verbose flag (if set) */
 
464
        if (task_plugin_param & CPU_BIND_VERBOSE)
 
465
                *flags |= CPU_BIND_VERBOSE;
 
466
 
 
467
        buf = xstrdup(arg);
 
468
        p = buf;
 
469
        /* change all ',' delimiters not followed by a digit to ';'  */
 
470
        /* simplifies parsing tokens while keeping map/mask together */
 
471
        while (p[0] != '\0') {
 
472
                if ((p[0] == ',') && (!_isvalue(&(p[1]))))
 
473
                        p[0] = ';';
 
474
                p++;
 
475
        }
 
476
 
 
477
        p = buf;
 
478
        while ((tok = strsep(&p, ";"))) {
 
479
                if (strcasecmp(tok, "help") == 0) {
 
480
                        slurm_print_cpu_bind_help();
 
481
                        return 1;
 
482
                } else if ((strcasecmp(tok, "q") == 0) ||
 
483
                           (strcasecmp(tok, "quiet") == 0)) {
 
484
                        *flags &= ~CPU_BIND_VERBOSE;
 
485
                } else if ((strcasecmp(tok, "v") == 0) ||
 
486
                           (strcasecmp(tok, "verbose") == 0)) {
 
487
                        *flags |= CPU_BIND_VERBOSE;
 
488
                } else if ((strcasecmp(tok, "no") == 0) ||
 
489
                           (strcasecmp(tok, "none") == 0)) {
 
490
                        _clear_then_set((int *)flags, bind_bits, CPU_BIND_NONE);
 
491
                        xfree(*cpu_bind);
 
492
                } else if (strcasecmp(tok, "rank") == 0) {
 
493
                        _clear_then_set((int *)flags, bind_bits, CPU_BIND_RANK);
 
494
                        xfree(*cpu_bind);
 
495
                } else if ((strncasecmp(tok, "map_cpu", 7) == 0) ||
 
496
                           (strncasecmp(tok, "mapcpu", 6) == 0)) {
 
497
                        char *list;
 
498
                        list = strsep(&tok, ":=");
 
499
                        list = strsep(&tok, ":=");
 
500
                        _clear_then_set((int *)flags, bind_bits, CPU_BIND_MAP);
 
501
                        xfree(*cpu_bind);
 
502
                        if (list && *list) {
 
503
                                *cpu_bind = xstrdup(list);
 
504
                        } else {
 
505
                                error("missing list for \"--cpu_bind="
 
506
                                      "map_cpu:<list>\"");
 
507
                                xfree(buf);
 
508
                                return 1;
 
509
                        }
 
510
                } else if ((strncasecmp(tok, "mask_cpu", 8) == 0) ||
 
511
                           (strncasecmp(tok, "maskcpu", 7) == 0)) {
 
512
                        char *list;
 
513
                        list = strsep(&tok, ":=");
 
514
                        list = strsep(&tok, ":=");
 
515
                        _clear_then_set((int *)flags, bind_bits, CPU_BIND_MASK);
 
516
                        xfree(*cpu_bind);
 
517
                        if (list && *list) {
 
518
                                *cpu_bind = xstrdup(list);
 
519
                        } else {
 
520
                                error("missing list for \"--cpu_bind="
 
521
                                      "mask_cpu:<list>\"");
 
522
                                xfree(buf);
 
523
                                return -1;
 
524
                        }
 
525
                } else if (strcasecmp(tok, "rank_ldom") == 0) {
 
526
                        _clear_then_set((int *)flags, bind_bits,
 
527
                                        CPU_BIND_LDRANK);
 
528
                        xfree(*cpu_bind);
 
529
                } else if ((strncasecmp(tok, "map_ldom", 8) == 0) ||
 
530
                           (strncasecmp(tok, "mapldom", 7) == 0)) {
 
531
                        char *list;
 
532
                        list = strsep(&tok, ":=");
 
533
                        list = strsep(&tok, ":=");
 
534
                        _clear_then_set((int *)flags, bind_bits,
 
535
                                        CPU_BIND_LDMAP);
 
536
                        xfree(*cpu_bind);
 
537
                        if (list && *list) {
 
538
                                *cpu_bind = xstrdup(list);
 
539
                        } else {
 
540
                                error("missing list for \"--cpu_bind="
 
541
                                      "map_ldom:<list>\"");
 
542
                                xfree(buf);
 
543
                                return 1;
 
544
                        }
 
545
                } else if ((strncasecmp(tok, "mask_ldom", 9) == 0) ||
 
546
                           (strncasecmp(tok, "maskldom", 8) == 0)) {
 
547
                        char *list;
 
548
                        list = strsep(&tok, ":=");
 
549
                        list = strsep(&tok, ":=");
 
550
                        _clear_then_set((int *)flags, bind_bits,
 
551
                                        CPU_BIND_LDMASK);
 
552
                        xfree(*cpu_bind);
 
553
                        if (list && *list) {
 
554
                                *cpu_bind = xstrdup(list);
 
555
                        } else {
 
556
                                error("missing list for \"--cpu_bind="
 
557
                                      "mask_ldom:<list>\"");
 
558
                                xfree(buf);
 
559
                                return -1;
 
560
                        }
 
561
                } else if ((strcasecmp(tok, "socket") == 0) ||
 
562
                           (strcasecmp(tok, "sockets") == 0)) {
 
563
                        if (task_plugin_param & 
 
564
                            (CPU_BIND_NONE | CPU_BIND_TO_CORES | 
 
565
                             CPU_BIND_TO_THREADS | CPU_BIND_TO_LDOMS)) {
 
566
                                error("--cpu_bind=sockets incompatable with "
 
567
                                      "TaskPluginParam configuration "
 
568
                                      "parameter");
 
569
                                return -1;
 
570
                        }
 
571
                        _clear_then_set((int *)flags, bind_to_bits,
 
572
                                       CPU_BIND_TO_SOCKETS);
 
573
                } else if ((strcasecmp(tok, "core") == 0) ||
 
574
                           (strcasecmp(tok, "cores") == 0)) {
 
575
                        if (task_plugin_param & 
 
576
                            (CPU_BIND_NONE | CPU_BIND_TO_SOCKETS | 
 
577
                             CPU_BIND_TO_THREADS | CPU_BIND_TO_LDOMS)) {
 
578
                                error("--cpu_bind=cores incompatable with "
 
579
                                      "TaskPluginParam configuration "
 
580
                                      "parameter");
 
581
                                return -1;
 
582
                        }
 
583
                        _clear_then_set((int *)flags, bind_to_bits,
 
584
                                       CPU_BIND_TO_CORES);
 
585
                } else if ((strcasecmp(tok, "thread") == 0) ||
 
586
                           (strcasecmp(tok, "threads") == 0)) {
 
587
                        if (task_plugin_param & 
 
588
                            (CPU_BIND_NONE | CPU_BIND_TO_SOCKETS | 
 
589
                             CPU_BIND_TO_CORES | CPU_BIND_TO_LDOMS)) {
 
590
                                error("--cpu_bind=threads incompatable with "
 
591
                                      "TaskPluginParam configuration "
 
592
                                      "parameter");
 
593
                                return -1;
 
594
                        }
 
595
                        _clear_then_set((int *)flags, bind_to_bits,
 
596
                                       CPU_BIND_TO_THREADS);
 
597
                } else if ((strcasecmp(tok, "ldom") == 0) ||
 
598
                           (strcasecmp(tok, "ldoms") == 0)) {
 
599
                        if (task_plugin_param & 
 
600
                            (CPU_BIND_NONE | CPU_BIND_TO_SOCKETS | 
 
601
                             CPU_BIND_TO_CORES | CPU_BIND_TO_THREADS)) {
 
602
                                error("--cpu_bind=threads incompatable with "
 
603
                                      "TaskPluginParam configuration "
 
604
                                      "parameter");
 
605
                                return -1;
 
606
                        }
 
607
                        _clear_then_set((int *)flags, bind_to_bits,
 
608
                                       CPU_BIND_TO_LDOMS);
 
609
                } else {
 
610
                        error("unrecognized --cpu_bind argument \"%s\"", tok);
 
611
                        xfree(buf);
 
612
                        return -1;
 
613
                }
 
614
        }
 
615
        xfree(buf);
 
616
 
 
617
        return 0;
 
618
}
 
619
 
 
620
void slurm_print_mem_bind_help(void)
 
621
{
 
622
                        printf(
 
623
"Memory bind options:\n"
 
624
"    --mem_bind=         Bind memory to locality domains (ldom)\n"
 
625
"        q[uiet]         quietly bind before task runs (default)\n"
 
626
"        v[erbose]       verbosely report binding before task runs\n"
 
627
"        no[ne]          don't bind tasks to memory (default)\n"
 
628
"        rank            bind by task rank\n"
 
629
"        local           bind to memory local to processor\n"
 
630
"        map_mem:<list>  specify a memory binding for each task\n"
 
631
"                        where <list> is <cpuid1>,<cpuid2>,...<cpuidN>\n"
 
632
"        mask_mem:<list> specify a memory binding mask for each tasks\n"
 
633
"                        where <list> is <mask1>,<mask2>,...<maskN>\n"
 
634
"        help            show this help message\n");
 
635
}
 
636
 
 
637
/*
 
638
 * verify mem_bind arguments
 
639
 *
 
640
 * we support different memory binding names
 
641
 * we also allow a verbose setting to be specified
 
642
 *     --mem_bind=v
 
643
 *     --mem_bind=rank,v
 
644
 *     --mem_bind=rank
 
645
 *     --mem_bind={MAP_MEM|MASK_MEM}:0,1,2,3,4
 
646
 *
 
647
 * returns -1 on error, 0 otherwise
 
648
 */
 
649
int slurm_verify_mem_bind(const char *arg, char **mem_bind, 
 
650
                          mem_bind_type_t *flags)
 
651
{
 
652
        char *buf, *p, *tok;
 
653
        int bind_bits = MEM_BIND_NONE|MEM_BIND_RANK|MEM_BIND_LOCAL|
 
654
                MEM_BIND_MAP|MEM_BIND_MASK;
 
655
 
 
656
        if (arg == NULL) {
 
657
                return 0;
 
658
        }
 
659
 
 
660
        buf = xstrdup(arg);
 
661
        p = buf;
 
662
        /* change all ',' delimiters not followed by a digit to ';'  */
 
663
        /* simplifies parsing tokens while keeping map/mask together */
 
664
        while (p[0] != '\0') {
 
665
                if ((p[0] == ',') && (!_isvalue(&(p[1]))))
 
666
                        p[0] = ';';
 
667
                p++;
 
668
        }
 
669
 
 
670
        p = buf;
 
671
        while ((tok = strsep(&p, ";"))) {
 
672
                if (strcasecmp(tok, "help") == 0) {
 
673
                        slurm_print_mem_bind_help();
 
674
                        return 1;
 
675
                        
 
676
                } else if ((strcasecmp(tok, "q") == 0) ||
 
677
                           (strcasecmp(tok, "quiet") == 0)) {
 
678
                        *flags &= ~MEM_BIND_VERBOSE;
 
679
                } else if ((strcasecmp(tok, "v") == 0) ||
 
680
                           (strcasecmp(tok, "verbose") == 0)) {
 
681
                        *flags |= MEM_BIND_VERBOSE;
 
682
                } else if ((strcasecmp(tok, "no") == 0) ||
 
683
                           (strcasecmp(tok, "none") == 0)) {
 
684
                        _clear_then_set((int *)flags, bind_bits, MEM_BIND_NONE);
 
685
                        xfree(*mem_bind);
 
686
                } else if (strcasecmp(tok, "rank") == 0) {
 
687
                        _clear_then_set((int *)flags, bind_bits, MEM_BIND_RANK);
 
688
                        xfree(*mem_bind);
 
689
                } else if (strcasecmp(tok, "local") == 0) {
 
690
                        _clear_then_set((int *)flags, bind_bits, MEM_BIND_LOCAL);
 
691
                        xfree(*mem_bind);
 
692
                } else if ((strncasecmp(tok, "map_mem", 7) == 0) ||
 
693
                           (strncasecmp(tok, "mapmem", 6) == 0)) {
 
694
                        char *list;
 
695
                        list = strsep(&tok, ":=");
 
696
                        list = strsep(&tok, ":=");
 
697
                        _clear_then_set((int *)flags, bind_bits, MEM_BIND_MAP);
 
698
                        xfree(*mem_bind);
 
699
                        if (list && *list) {
 
700
                                *mem_bind = xstrdup(list);
 
701
                        } else {
 
702
                                error("missing list for \"--mem_bind=map_mem:<list>\"");
 
703
                                xfree(buf);
 
704
                                return 1;
 
705
                        }
 
706
                } else if ((strncasecmp(tok, "mask_mem", 8) == 0) ||
 
707
                           (strncasecmp(tok, "maskmem", 7) == 0)) {
 
708
                        char *list;
 
709
                        list = strsep(&tok, ":=");
 
710
                        list = strsep(&tok, ":=");
 
711
                        _clear_then_set((int *)flags, bind_bits, MEM_BIND_MASK);
 
712
                        xfree(*mem_bind);
 
713
                        if (list && *list) {
 
714
                                *mem_bind = xstrdup(list);
 
715
                        } else {
 
716
                                error("missing list for \"--mem_bind=mask_mem:<list>\"");
 
717
                                xfree(buf);
 
718
                                return 1;
 
719
                        }
 
720
                } else {
 
721
                        error("unrecognized --mem_bind argument \"%s\"", tok);
 
722
                        xfree(buf);
 
723
                        return 1;
 
724
                }
 
725
        }
 
726
 
 
727
        xfree(buf);
 
728
        return 0;
 
729
}