Subversion Repositories oidplus

Rev

Rev 1101 | Rev 1324 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * Pure-PHP arbitrary precision integer arithmetic library.
5
 *
6
 * Supports base-2, base-10, base-16, and base-256 numbers.  Uses the GMP or BCMath extensions, if available,
7
 * and an internal implementation, otherwise.
8
 *
9
 * PHP version 5 and 7
10
 *
11
 * Here's an example of how to use this library:
12
 * <code>
13
 * <?php
14
 *    $a = new \phpseclib3\Math\BigInteger(2);
15
 *    $b = new \phpseclib3\Math\BigInteger(3);
16
 *
17
 *    $c = $a->add($b);
18
 *
19
 *    echo $c->toString(); // outputs 5
20
 * ?>
21
 * </code>
22
 *
23
 * @author    Jim Wigginton <terrafrost@php.net>
24
 * @copyright 2017 Jim Wigginton
25
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
26
 */
27
 
28
namespace phpseclib3\Math;
29
 
30
use phpseclib3\Exception\BadConfigurationException;
31
use phpseclib3\Math\BigInteger\Engines\Engine;
32
 
33
/**
34
 * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
35
 * numbers.
36
 *
37
 * @author  Jim Wigginton <terrafrost@php.net>
38
 */
39
class BigInteger implements \JsonSerializable
40
{
41
    /**
42
     * Main Engine
43
     *
44
     * @var class-string<Engine>
45
     */
46
    private static $mainEngine;
47
 
48
    /**
49
     * Selected Engines
50
     *
51
     * @var list<string>
52
     */
53
    private static $engines;
54
 
55
    /**
56
     * The actual BigInteger object
57
     *
58
     * @var object
59
     */
60
    private $value;
61
 
62
    /**
63
     * Mode independent value used for serialization.
64
     *
65
     * @see self::__sleep()
66
     * @see self::__wakeup()
67
     * @var string
68
     */
69
    private $hex;
70
 
71
    /**
72
     * Precision (used only for serialization)
73
     *
74
     * @see self::__sleep()
75
     * @see self::__wakeup()
76
     * @var int
77
     */
78
    private $precision;
79
 
80
    /**
81
     * Sets engine type.
82
     *
83
     * Throws an exception if the type is invalid
84
     *
85
     * @param string $main
86
     * @param list<string> $modexps optional
87
     * @return void
88
     */
89
    public static function setEngine($main, array $modexps = ['DefaultEngine'])
90
    {
91
        self::$engines = [];
92
 
93
        $fqmain = 'phpseclib3\\Math\\BigInteger\\Engines\\' . $main;
94
        if (!class_exists($fqmain) || !method_exists($fqmain, 'isValidEngine')) {
95
            throw new \InvalidArgumentException("$main is not a valid engine");
96
        }
97
        if (!$fqmain::isValidEngine()) {
98
            throw new BadConfigurationException("$main is not setup correctly on this system");
99
        }
100
        /** @var class-string<Engine> $fqmain */
101
        self::$mainEngine = $fqmain;
102
 
103
        if (!in_array('Default', $modexps)) {
104
            $modexps[] = 'DefaultEngine';
105
        }
106
 
107
        $found = false;
108
        foreach ($modexps as $modexp) {
109
            try {
110
                $fqmain::setModExpEngine($modexp);
111
                $found = true;
112
                break;
113
            } catch (\Exception $e) {
114
            }
115
        }
116
 
117
        if (!$found) {
118
            throw new BadConfigurationException("No valid modular exponentiation engine found for $main");
119
        }
120
 
121
        self::$engines = [$main, $modexp];
122
    }
123
 
124
    /**
125
     * Returns the engine type
126
     *
127
     * @return string[]
128
     */
129
    public static function getEngine()
130
    {
131
        self::initialize_static_variables();
132
 
133
        return self::$engines;
134
    }
135
 
136
    /**
137
     * Initialize static variables
138
     */
139
    private static function initialize_static_variables()
140
    {
141
        if (!isset(self::$mainEngine)) {
142
            $engines = [
143
                ['GMP'],
144
                ['PHP64', ['OpenSSL']],
145
                ['BCMath', ['OpenSSL']],
146
                ['PHP32', ['OpenSSL']]
147
            ];
148
            foreach ($engines as $engine) {
149
                try {
150
                    self::setEngine($engine[0], isset($engine[1]) ? $engine[1] : []);
151
                    break;
152
                } catch (\Exception $e) {
153
                }
154
            }
155
        }
156
    }
157
 
158
    /**
159
     * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
160
     *
161
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
162
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
163
     *
164
     * @param string|int|BigInteger\Engines\Engine $x Base-10 number or base-$base number if $base set.
165
     * @param int $base
166
     */
167
    public function __construct($x = 0, $base = 10)
168
    {
169
        self::initialize_static_variables();
170
 
171
        if ($x instanceof self::$mainEngine) {
172
            $this->value = clone $x;
173
        } elseif ($x instanceof BigInteger\Engines\Engine) {
174
            $this->value = new static("$x");
175
            $this->value->setPrecision($x->getPrecision());
176
        } else {
177
            $this->value = new self::$mainEngine($x, $base);
178
        }
179
    }
180
 
181
    /**
182
     * Converts a BigInteger to a base-10 number.
183
     *
184
     * @return string
185
     */
186
    public function toString()
187
    {
188
        return $this->value->toString();
189
    }
190
 
191
    /**
192
     *  __toString() magic method
193
     */
194
    public function __toString()
195
    {
196
        return (string)$this->value;
197
    }
198
 
199
    /**
200
     *  __debugInfo() magic method
201
     *
202
     * Will be called, automatically, when print_r() or var_dump() are called
203
     */
204
    public function __debugInfo()
205
    {
206
        return $this->value->__debugInfo();
207
    }
208
 
209
    /**
210
     * Converts a BigInteger to a byte string (eg. base-256).
211
     *
212
     * @param bool $twos_compliment
213
     * @return string
214
     */
215
    public function toBytes($twos_compliment = false)
216
    {
217
        return $this->value->toBytes($twos_compliment);
218
    }
219
 
220
    /**
221
     * Converts a BigInteger to a hex string (eg. base-16).
222
     *
223
     * @param bool $twos_compliment
224
     * @return string
225
     */
226
    public function toHex($twos_compliment = false)
227
    {
228
        return $this->value->toHex($twos_compliment);
229
    }
230
 
231
    /**
232
     * Converts a BigInteger to a bit string (eg. base-2).
233
     *
234
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
235
     * saved as two's compliment.
236
     *
237
     * @param bool $twos_compliment
238
     * @return string
239
     */
240
    public function toBits($twos_compliment = false)
241
    {
242
        return $this->value->toBits($twos_compliment);
243
    }
244
 
245
    /**
246
     * Adds two BigIntegers.
247
     *
248
     * @param BigInteger $y
249
     * @return BigInteger
250
     */
251
    public function add(BigInteger $y)
252
    {
253
        return new static($this->value->add($y->value));
254
    }
255
 
256
    /**
257
     * Subtracts two BigIntegers.
258
     *
259
     * @param BigInteger $y
260
     * @return BigInteger
261
     */
262
    public function subtract(BigInteger $y)
263
    {
264
        return new static($this->value->subtract($y->value));
265
    }
266
 
267
    /**
268
     * Multiplies two BigIntegers
269
     *
270
     * @param BigInteger $x
271
     * @return BigInteger
272
     */
273
    public function multiply(BigInteger $x)
274
    {
275
        return new static($this->value->multiply($x->value));
276
    }
277
 
278
    /**
279
     * Divides two BigIntegers.
280
     *
281
     * Returns an array whose first element contains the quotient and whose second element contains the
282
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
283
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
284
     * and the divisor (basically, the "common residue" is the first positive modulo).
285
     *
286
     * Here's an example:
287
     * <code>
288
     * <?php
289
     *    $a = new \phpseclib3\Math\BigInteger('10');
290
     *    $b = new \phpseclib3\Math\BigInteger('20');
291
     *
292
     *    list($quotient, $remainder) = $a->divide($b);
293
     *
294
     *    echo $quotient->toString(); // outputs 0
295
     *    echo "\r\n";
296
     *    echo $remainder->toString(); // outputs 10
297
     * ?>
298
     * </code>
299
     *
300
     * @param BigInteger $y
301
     * @return BigInteger[]
302
     */
303
    public function divide(BigInteger $y)
304
    {
305
        list($q, $r) = $this->value->divide($y->value);
306
        return [
307
            new static($q),
308
            new static($r)
309
        ];
310
    }
311
 
312
    /**
313
     * Calculates modular inverses.
314
     *
315
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
316
     *
317
     * @param BigInteger $n
318
     * @return BigInteger
319
     */
320
    public function modInverse(BigInteger $n)
321
    {
322
        return new static($this->value->modInverse($n->value));
323
    }
324
 
325
    /**
326
     * Calculates modular inverses.
327
     *
328
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
329
     *
330
     * @param BigInteger $n
331
     * @return BigInteger[]
332
     */
333
    public function extendedGCD(BigInteger $n)
334
    {
335
        extract($this->value->extendedGCD($n->value));
336
        /**
337
         * @var BigInteger $gcd
338
         * @var BigInteger $x
339
         * @var BigInteger $y
340
         */
341
        return [
342
            'gcd' => new static($gcd),
343
            'x' => new static($x),
344
            'y' => new static($y)
345
        ];
346
    }
347
 
348
    /**
349
     * Calculates the greatest common divisor
350
     *
351
     * Say you have 693 and 609.  The GCD is 21.
352
     *
353
     * @param BigInteger $n
354
     * @return BigInteger
355
     */
356
    public function gcd(BigInteger $n)
357
    {
358
        return new static($this->value->gcd($n->value));
359
    }
360
 
361
    /**
362
     * Absolute value.
363
     *
364
     * @return BigInteger
365
     */
366
    public function abs()
367
    {
368
        return new static($this->value->abs());
369
    }
370
 
371
    /**
372
     * Set Precision
373
     *
374
     * Some bitwise operations give different results depending on the precision being used.  Examples include left
375
     * shift, not, and rotates.
376
     *
377
     * @param int $bits
378
     */
379
    public function setPrecision($bits)
380
    {
381
        $this->value->setPrecision($bits);
382
    }
383
 
384
    /**
385
     * Get Precision
386
     *
387
     * Returns the precision if it exists, false if it doesn't
388
     *
389
     * @return int|bool
390
     */
391
    public function getPrecision()
392
    {
393
        return $this->value->getPrecision();
394
    }
395
 
396
    /**
397
     * Serialize
398
     *
399
     * Will be called, automatically, when serialize() is called on a BigInteger object.
400
     *
401
     * __sleep() / __wakeup() have been around since PHP 4.0
402
     *
403
     * \Serializable was introduced in PHP 5.1 and deprecated in PHP 8.1:
404
     * https://wiki.php.net/rfc/phase_out_serializable
405
     *
406
     * __serialize() / __unserialize() were introduced in PHP 7.4:
407
     * https://wiki.php.net/rfc/custom_object_serialization
408
     *
409
     * @return array
410
     */
411
    public function __sleep()
412
    {
413
        $this->hex = $this->toHex(true);
414
        $vars = ['hex'];
415
        if ($this->getPrecision() > 0) {
416
            $vars[] = 'precision';
417
        }
418
        return $vars;
419
    }
420
 
421
    /**
422
     * Serialize
423
     *
424
     * Will be called, automatically, when unserialize() is called on a BigInteger object.
425
     */
426
    public function __wakeup()
427
    {
428
        $temp = new static($this->hex, -16);
429
        $this->value = $temp->value;
430
        if ($this->precision > 0) {
431
            // recalculate $this->bitmask
432
            $this->setPrecision($this->precision);
433
        }
434
    }
435
 
436
    /**
437
     * JSON Serialize
438
     *
439
     * Will be called, automatically, when json_encode() is called on a BigInteger object.
1114 daniel-mar 440
     *
441
     * @return array{hex: string, precision?: int]
827 daniel-mar 442
     */
443
    #[\ReturnTypeWillChange]
444
    public function jsonSerialize()
445
    {
446
        $result = ['hex' => $this->toHex(true)];
447
        if ($this->precision > 0) {
448
            $result['precision'] = $this->getPrecision();
449
        }
450
        return $result;
451
    }
452
 
453
    /**
454
     * Performs modular exponentiation.
455
     *
456
     * @param BigInteger $e
457
     * @param BigInteger $n
458
     * @return BigInteger
459
     */
460
    public function powMod(BigInteger $e, BigInteger $n)
461
    {
462
        return new static($this->value->powMod($e->value, $n->value));
463
    }
464
 
465
    /**
466
     * Performs modular exponentiation.
467
     *
468
     * @param BigInteger $e
469
     * @param BigInteger $n
470
     * @return BigInteger
471
     */
472
    public function modPow(BigInteger $e, BigInteger $n)
473
    {
474
        return new static($this->value->modPow($e->value, $n->value));
475
    }
476
 
477
    /**
478
     * Compares two numbers.
479
     *
480
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
481
     * is demonstrated thusly:
482
     *
483
     * $x  > $y: $x->compare($y)  > 0
484
     * $x  < $y: $x->compare($y)  < 0
485
     * $x == $y: $x->compare($y) == 0
486
     *
487
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
488
     *
489
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
490
     *
491
     * @param BigInteger $y
492
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
493
     * @see self::equals()
494
     */
495
    public function compare(BigInteger $y)
496
    {
497
        return $this->value->compare($y->value);
498
    }
499
 
500
    /**
501
     * Tests the equality of two numbers.
502
     *
503
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
504
     *
505
     * @param BigInteger $x
506
     * @return bool
507
     */
508
    public function equals(BigInteger $x)
509
    {
510
        return $this->value->equals($x->value);
511
    }
512
 
513
    /**
514
     * Logical Not
515
     *
516
     * @return BigInteger
517
     */
518
    public function bitwise_not()
519
    {
520
        return new static($this->value->bitwise_not());
521
    }
522
 
523
    /**
524
     * Logical And
525
     *
526
     * @param BigInteger $x
527
     * @return BigInteger
528
     */
529
    public function bitwise_and(BigInteger $x)
530
    {
531
        return new static($this->value->bitwise_and($x->value));
532
    }
533
 
534
    /**
535
     * Logical Or
536
     *
537
     * @param BigInteger $x
538
     * @return BigInteger
539
     */
540
    public function bitwise_or(BigInteger $x)
541
    {
542
        return new static($this->value->bitwise_or($x->value));
543
    }
544
 
545
    /**
546
     * Logical Exclusive Or
547
     *
548
     * @param BigInteger $x
549
     * @return BigInteger
550
     */
551
    public function bitwise_xor(BigInteger $x)
552
    {
553
        return new static($this->value->bitwise_xor($x->value));
554
    }
555
 
556
    /**
557
     * Logical Right Shift
558
     *
559
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
560
     *
561
     * @param int $shift
562
     * @return BigInteger
563
     */
564
    public function bitwise_rightShift($shift)
565
    {
566
        return new static($this->value->bitwise_rightShift($shift));
567
    }
568
 
569
    /**
570
     * Logical Left Shift
571
     *
572
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
573
     *
574
     * @param int $shift
575
     * @return BigInteger
576
     */
577
    public function bitwise_leftShift($shift)
578
    {
579
        return new static($this->value->bitwise_leftShift($shift));
580
    }
581
 
582
    /**
583
     * Logical Left Rotate
584
     *
585
     * Instead of the top x bits being dropped they're appended to the shifted bit string.
586
     *
587
     * @param int $shift
588
     * @return BigInteger
589
     */
590
    public function bitwise_leftRotate($shift)
591
    {
592
        return new static($this->value->bitwise_leftRotate($shift));
593
    }
594
 
595
    /**
596
     * Logical Right Rotate
597
     *
598
     * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
599
     *
600
     * @param int $shift
601
     * @return BigInteger
602
     */
603
    public function bitwise_rightRotate($shift)
604
    {
605
        return new static($this->value->bitwise_rightRotate($shift));
606
    }
607
 
608
    /**
609
     * Returns the smallest and largest n-bit number
610
     *
611
     * @param int $bits
612
     * @return BigInteger[]
613
     */
614
    public static function minMaxBits($bits)
615
    {
616
        self::initialize_static_variables();
617
 
618
        $class = self::$mainEngine;
619
        extract($class::minMaxBits($bits));
620
        /** @var BigInteger $min
621
         * @var BigInteger $max
622
         */
623
        return [
624
            'min' => new static($min),
625
            'max' => new static($max)
626
        ];
627
    }
628
 
629
    /**
630
     * Return the size of a BigInteger in bits
631
     *
632
     * @return int
633
     */
634
    public function getLength()
635
    {
636
        return $this->value->getLength();
637
    }
638
 
639
    /**
640
     * Return the size of a BigInteger in bytes
641
     *
642
     * @return int
643
     */
644
    public function getLengthInBytes()
645
    {
646
        return $this->value->getLengthInBytes();
647
    }
648
 
649
    /**
650
     * Generates a random number of a certain size
651
     *
652
     * Bit length is equal to $size
653
     *
654
     * @param int $size
655
     * @return BigInteger
656
     */
657
    public static function random($size)
658
    {
659
        self::initialize_static_variables();
660
 
661
        $class = self::$mainEngine;
662
        return new static($class::random($size));
663
    }
664
 
665
    /**
666
     * Generates a random prime number of a certain size
667
     *
668
     * Bit length is equal to $size
669
     *
670
     * @param int $size
671
     * @return BigInteger
672
     */
673
    public static function randomPrime($size)
674
    {
675
        self::initialize_static_variables();
676
 
677
        $class = self::$mainEngine;
678
        return new static($class::randomPrime($size));
679
    }
680
 
681
    /**
682
     * Generate a random prime number between a range
683
     *
684
     * If there's not a prime within the given range, false will be returned.
685
     *
686
     * @param BigInteger $min
687
     * @param BigInteger $max
688
     * @return false|BigInteger
689
     */
690
    public static function randomRangePrime(BigInteger $min, BigInteger $max)
691
    {
692
        $class = self::$mainEngine;
693
        return new static($class::randomRangePrime($min->value, $max->value));
694
    }
695
 
696
    /**
697
     * Generate a random number between a range
698
     *
699
     * Returns a random number between $min and $max where $min and $max
700
     * can be defined using one of the two methods:
701
     *
702
     * BigInteger::randomRange($min, $max)
703
     * BigInteger::randomRange($max, $min)
704
     *
705
     * @param BigInteger $min
706
     * @param BigInteger $max
707
     * @return BigInteger
708
     */
709
    public static function randomRange(BigInteger $min, BigInteger $max)
710
    {
711
        $class = self::$mainEngine;
712
        return new static($class::randomRange($min->value, $max->value));
713
    }
714
 
715
    /**
716
     * Checks a numer to see if it's prime
717
     *
718
     * Assuming the $t parameter is not set, this function has an error rate of 2**-80.  The main motivation for the
719
     * $t parameter is distributability.  BigInteger::randomPrime() can be distributed across multiple pageloads
720
     * on a website instead of just one.
721
     *
722
     * @param int|bool $t
723
     * @return bool
724
     */
725
    public function isPrime($t = false)
726
    {
727
        return $this->value->isPrime($t);
728
    }
729
 
730
    /**
731
     * Calculates the nth root of a biginteger.
732
     *
733
     * Returns the nth root of a positive biginteger, where n defaults to 2
734
     *
735
     * @param int $n optional
736
     * @return BigInteger
737
     */
738
    public function root($n = 2)
739
    {
740
        return new static($this->value->root($n));
741
    }
742
 
743
    /**
744
     * Performs exponentiation.
745
     *
746
     * @param BigInteger $n
747
     * @return BigInteger
748
     */
749
    public function pow(BigInteger $n)
750
    {
751
        return new static($this->value->pow($n->value));
752
    }
753
 
754
    /**
755
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
756
     *
757
     * @param BigInteger ...$nums
758
     * @return BigInteger
759
     */
760
    public static function min(BigInteger ...$nums)
761
    {
762
        $class = self::$mainEngine;
763
        $nums = array_map(function ($num) {
764
            return $num->value;
765
        }, $nums);
766
        return new static($class::min(...$nums));
767
    }
768
 
769
    /**
770
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
771
     *
772
     * @param BigInteger ...$nums
773
     * @return BigInteger
774
     */
775
    public static function max(BigInteger ...$nums)
776
    {
777
        $class = self::$mainEngine;
778
        $nums = array_map(function ($num) {
779
            return $num->value;
780
        }, $nums);
781
        return new static($class::max(...$nums));
782
    }
783
 
784
    /**
785
     * Tests BigInteger to see if it is between two integers, inclusive
786
     *
787
     * @param BigInteger $min
788
     * @param BigInteger $max
789
     * @return bool
790
     */
791
    public function between(BigInteger $min, BigInteger $max)
792
    {
793
        return $this->value->between($min->value, $max->value);
794
    }
795
 
796
    /**
797
     * Clone
798
     */
799
    public function __clone()
800
    {
801
        $this->value = clone $this->value;
802
    }
803
 
804
    /**
805
     * Is Odd?
806
     *
807
     * @return bool
808
     */
809
    public function isOdd()
810
    {
811
        return $this->value->isOdd();
812
    }
813
 
814
    /**
815
     * Tests if a bit is set
816
     *
817
     * @param int $x
818
     * @return bool
819
     */
820
    public function testBit($x)
821
    {
822
        return $this->value->testBit($x);
823
    }
824
 
825
    /**
826
     * Is Negative?
827
     *
828
     * @return bool
829
     */
830
    public function isNegative()
831
    {
832
        return $this->value->isNegative();
833
    }
834
 
835
    /**
836
     * Negate
837
     *
838
     * Given $k, returns -$k
839
     *
840
     * @return BigInteger
841
     */
842
    public function negate()
843
    {
844
        return new static($this->value->negate());
845
    }
846
 
847
    /**
848
     * Scan for 1 and right shift by that amount
849
     *
850
     * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
851
     *
852
     * @param BigInteger $r
853
     * @return int
854
     */
855
    public static function scan1divide(BigInteger $r)
856
    {
857
        $class = self::$mainEngine;
858
        return $class::scan1divide($r->value);
859
    }
860
 
861
    /**
862
     * Create Recurring Modulo Function
863
     *
864
     * Sometimes it may be desirable to do repeated modulos with the same number outside of
865
     * modular exponentiation
866
     *
867
     * @return callable
868
     */
869
    public function createRecurringModuloFunction()
870
    {
871
        $func = $this->value->createRecurringModuloFunction();
872
        return function (BigInteger $x) use ($func) {
873
            return new static($func($x->value));
874
        };
875
    }
876
 
877
    /**
878
     * Bitwise Split
879
     *
880
     * Splits BigInteger's into chunks of $split bits
881
     *
882
     * @param int $split
883
     * @return BigInteger[]
884
     */
885
    public function bitwise_split($split)
886
    {
887
        return array_map(function ($val) {
888
            return new static($val);
889
        }, $this->value->bitwise_split($split));
890
    }
1114 daniel-mar 891
}