Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125081 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 8F6661A00BD for ; Wed, 21 Aug 2024 01:59:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1724205677; bh=8I0o25fi67Jr6ylAxpgO3TI4xMLzkfeDVUQfZTc1nh4=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=HUYZAJDenXWZE9UX5J0BMYre5QgdwxB6+Yf/d+wduzjPjiSCim0CNCSY6cRp53ZCt NiVelE+WVeBNt68DBxa6Lyzs8/FRymDeF5QNzgmXV+CM/ojvXykqJD0ProWwBCGgtb 0cTM2a2ts0mKeCFr643nB11+8KqPEaovKGdivE9Q6Ri2+jHUz2YjAKGYk0yzGUsre9 zhV9OTCUJ2xu7YSt16FuMmd1t2X/2z2/gactfxKjK8ZICzf10xZZ7EDn9n4ZIljN1G 8J+OpGdUJJMynUoq7vVjj6zlDG9kx6sE78dyYSK4dFX2bP8FCMo1WzeRvCreM9idvv vR6PFY7h7RR4w== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5221E18006F for ; Wed, 21 Aug 2024 02:01:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.8 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05olkn2036.outbound.protection.outlook.com [40.92.90.36]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 21 Aug 2024 02:01:16 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=HBgth5GxywUJoaKexoVkD1ghIpuuxXkPEvzSXAD5VAiJV9hwNbVv6FINQT6XnmYjL7LhXlKKOfjKrKGBLCTq/rC2WStOXaFLX/niEjv8TfJAtm8d8wgz4A8IyPIXJP0fVKznpMsM2EDu6zxw1XSYtUqSkeMqN0DvABxyBAGdqe4WG3ngxOwHxrp/too5+QBshtlNnZEfYw8WkqsLBc1cRm/T8c7vtOMqDaaRoWTZpgT7Pbv4ZQ8AtOFWJBiyY49X/kdgYBZLLBvQSyqaA+K3xPXfeic/nsXEd8YU1LIZmNlcmerSfUwFiIeyjrOK2VU4ZqGpxn45nK0pe1i6DpIlvA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Acc1dJ4U9S1Ov08WZkfAKW0DGAtFERCJ0XHf0EV/d7A=; b=pnIJuQ2kIQbaqxIiXYaBV/RFVN5WCdAJ/5tF78ptMiDY4Lbshxrp6kXkr24xg4ejYB1jTSGSbJ6PlV2Z1wx4Gg02Q2B+kvHu+gAnzFSQRI0ub/kItbFsuN5co/WkJ9KjQ4CnlJziu9wKWv7ebgGWl27fj1ol+Hj/nRsvOKfWRETJNnJS2G7sSHrUv3+BmaxJKOHsm5anemmIsz4MumtD3un7ZVNgMJHHSkeuDp+T42GkdcIVAV+TwV3kPGMtGL314gBnRX++kinPHFoCeWSUN7/vp3V5r8O/jNW+Mfs1y150tCPUs22XA2Bq31HqBI4XoWS4Dqlnliuhj5aDJwDAnw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hotmail.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Acc1dJ4U9S1Ov08WZkfAKW0DGAtFERCJ0XHf0EV/d7A=; b=WN7817pVqQqtV7JaRI+MEe35XhsLLgYgatVEwIZO/FBKSZ0Whck8DSuWcn970jXVDiiPNhjsuTX9a8MCZeoRC3L1wQt27tfGkBAPXyZgonGovTqV09hSDliTPc724SniWK6C0/4hxaOVF6LjbH2V+QaOPN0+VH6j+BM7l1wNjyPCR0uMm1CllBXUV8+z0c3z4nPnb6XKPFfCxHrPCArL2k3BczJ+Zm6N75eatT0pbn5uLtCqZwpmRyvvsmA6GnBaX7XYgFaM4ecb3ZqBOkrKiQ1ChrbAg5Fjy1j0DcFDlYC2nvXkqmZ9ui7nOV2ztAl2jKSS7kzUjeSXt5GaLk3N7A== Received: from AM8P250MB0170.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:321::21) by PA2P250MB1069.EURP250.PROD.OUTLOOK.COM (2603:10a6:102:40d::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7875.21; Wed, 21 Aug 2024 01:59:24 +0000 Received: from AM8P250MB0170.EURP250.PROD.OUTLOOK.COM ([fe80::651e:bbd2:b18a:80ff]) by AM8P250MB0170.EURP250.PROD.OUTLOOK.COM ([fe80::651e:bbd2:b18a:80ff%6]) with mapi id 15.20.7875.019; Wed, 21 Aug 2024 01:59:24 +0000 Content-Type: multipart/alternative; boundary="------------bumRzmUSD3bwZBn3C8Uq67m1" Message-ID: Date: Wed, 21 Aug 2024 03:59:24 +0200 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] State of Generics and Collections To: Arnaud Le Blanc Cc: Derick Rethans , PHP Developers Mailing List References: <1b59392a-68cb-36eb-0fef-977ac7113520@php.net> Content-Language: en-US In-Reply-To: X-TMN: [vYSiKYH+coabt3qszx4J32IKOy/N0CqpJNjRFioXdj13tVVn53BBxZpu5Ae8Hg7x] X-ClientProxiedBy: AS4P190CA0061.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:656::22) To AM8P250MB0170.EURP250.PROD.OUTLOOK.COM (2603:10a6:20b:321::21) X-Microsoft-Original-Message-ID: <4971fd72-ebf1-4d11-80e6-0fcb429b1ccf@hotmail.com> Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM8P250MB0170:EE_|PA2P250MB1069:EE_ X-MS-Office365-Filtering-Correlation-Id: d5e866b0-5ecb-4050-99fe-08dcc184e156 X-Microsoft-Antispam: BCL:0;ARA:14566002|15080799003|19110799003|8060799006|12050799009|461199028|5072599009|3412199025|440099028; X-Microsoft-Antispam-Message-Info: 1zI9L9YBB716Zq4AbvjODATvgAzuT1bLZhvWonTn3aZ2niqGdhDMpVBkAxBanAn1CoWNVeeSM+EIO+7PuEbm8PcdtxIrqdKps0QcRBIDLUKx///xRL/RDOlcN8OnjV0Frmnz/hEJqjeRIOxy9tg1Wfq7XAiRWkF6BLqKQXtc1sfFfrMoCTs4KBo4hZzpEKUHW06KlJgDblFXVnNjP6VL/uuBYqLFI42Zg/x/K+FcKlRLeGPtjy0eTzeEvZfuYOe/qHv9+8m7/sjBZkicQhrQn/l1t/4CcehLUBuAs+EU8zHxpRh1wvKDRWXGJMF3u6khFL9ovjvrx72QRY2Oo7OcijM71PdPr24C587zkd0VgGI/3n4iyvcHZydh/C++t1+Kz24NpFclMqe+iOWOSp7HB5bXDh38sG0ARi6NJQAgt61EgUIR1J8D8t83QzEg9uJAw+X9YfNVOcbkiDQ/KJem5HMRjfMyq2hX/M0rUX5NnEUzrwRNqdvAx67/svOKy7XcyCH6L17xTMVUbJLh8aQcgiU1xZ4JyqFMGEkLOd3+3M8QZ5iibZdS28FlwERPpaZmIOYthE8S1HvcGf3iLoSStSS1L9mH83CF0f8DdzUfZN++JFqKHJ7awr/IbzDNtKPjnwgKwdR29xvciW8g6lO9jyiue0alv752kBUl85Vr7lXA67YOIXZGOZ/BvV7Vlg2gKPfZCVrx/8NdU9QajxiKdqGXCidDvZh+SV56ufpFEF59Ao/byqh7sLwKXmUu29SA X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?L3ZLQ3V0VmsySG1iOGpEUzhYclIvcGVGYzFMc05lclI4UWZ1RExXSTNoekN4?= =?utf-8?B?ZHd5MmE1aVNSVnB3dVNrNTQ0RWtTUEZzRHhJVWRmaFJSZWIwdVFaS1Jsb1hp?= =?utf-8?B?anE4cVpycGZMOHM2NjJBa0dobktZajJBS3JEYmdBM2MxZGZKK2poTStBNW1Y?= =?utf-8?B?THlURHhxclc0aU1wS1ozODArNjlyYWI3QkdKdmo5VDlDL29TUEh2aXZNOTVZ?= =?utf-8?B?RWwxaGE1bFVUdTRKOEZUbXJUT2JJRG5OR3RQK0Fxc25GeUttVEtJSzJ6ZTgr?= =?utf-8?B?T1dYY1QwazdpYWR1amd1U0lLSFNZamh6WnV5Q2NsMTNWVHBLUTFnL0JTbDM0?= =?utf-8?B?ZlBsZ3hmSkFRdVlKY0ZubnFlcklQQmVmeGJoT2JWK2JhZEdzTEJGMEVnUDFC?= =?utf-8?B?dTJyUU13VTF6SmFqNXI4WWhPNEhzNURGMG4vUkZ0ZkY3eW9NVTlXcVB5NlYx?= =?utf-8?B?bEVhRG41alNFM2xPbDlmR2l0V0VmZ0xscHgzYnQzNTVSVFpPam5NckpQMlMv?= =?utf-8?B?K2o3c056OW9RaEd5ZkJodEVwZVRHSVlEOEJQK0Z5TjNJenNPSEVyRGVzN0Uw?= =?utf-8?B?aGFhY1AyazNoY2lCUkxvV0JURGw4a0RyeWpTVFNLUnVOU1djNkExbEVvQ2RC?= =?utf-8?B?RUtIVFlDUHd5WDhSbDIrZytmUjI5RGFKQ2VmV2RmYlJwQmxORmMrOVdNOGt5?= =?utf-8?B?NHZHSGJhVlNKaVpaTUtOU0VjMVNxZkIrN2krTVFCZHB0cWoxRXhEMFRYallX?= =?utf-8?B?MlVSSGJKYXZsR1RsOE4rcm5PbkJ0Yys4N3pVUnZOWHpEVExkSXh5UFJJQ05v?= =?utf-8?B?a2srQ1kvTGNwTXVUazlwR0RPdlpkY0xyZnpqV0c1Q1VZTHI3NVIxNVRBWk9W?= =?utf-8?B?L2kydTFvcU1mNWVSV3hiY0FWVmlrL2QybzdiaGVWN1dzbzJSV1RjTEtzeUti?= =?utf-8?B?QTMyZ2E1Qk4vbWoraUJ1VnZ4aHlJbk0rNTY4eE1JWGFzUk12emUvWUlhbENz?= =?utf-8?B?ZHBSYnFWTnNFWTUvR1h1aHNRZHdzSGE2NGE3NFBKcFJya0JmUzVVUkhROE5N?= =?utf-8?B?SkQzRUdOVklnc3JDSWhnZUlRaURhOXVmRVNrK1YvSUhwUDdLTTNGc0Nza1NH?= =?utf-8?B?NTNrUURGQnFabkRZRDMrUkhVbEo0WHhGMTJtcjgzUmViSjVzbFZVUG9PQkRo?= =?utf-8?B?REdGenV0emtjc0tuU01ZU2l4cWNrVmZld0xTcU8vM3czZng1bHJIRHZNU2xZ?= =?utf-8?B?SjAvelBsNUpabDN1RUpMT0k0cEJ3N3V2UmZOb3h1TEJ2STZhdHBFaGI3dmd6?= =?utf-8?B?MmpxNDVKUCsyb01MS2M1bU1MRFc0bDF6YU4wMVlSVFhCYVY4N0M2RzZmbGdZ?= =?utf-8?B?b3d2ZVNLMUtIMzBQMVNPWG1Hc0k3TDJtVXM1aGF3blJTaklZcDkwNW95eXlN?= =?utf-8?B?bnhYMU9EamY5dGJmazNGNnVQTDRCcmcrS2grR2J1RG44MUgzNVRKTzk3ZVRN?= =?utf-8?B?QURLYjBZUFdsbFpTYUxLOExUVlZTb0RKeXdHRm9uVE1vZlpuc0FZQTMrMmtI?= =?utf-8?B?eGtUNDAvQXlmVFk0em42MmloZzR5dWo3dVFaLzQrZkJFYXpMalR6UTNRK0hB?= =?utf-8?B?ekorNWduM3RvV3FWN1ZKR01pcTRlMzN1d3JXY2l4aEhXb1ZuNTkvRndBanhO?= =?utf-8?B?bkdSSDBtd0RJVmRGYitYS2RyWUdvZUdSb25vbnlpdnZNdmZlNitIcFU0UWVV?= =?utf-8?Q?Qc+66sNTE0Ke0WbnkE=3D?= X-OriginatorOrg: sct-15-20-7784-11-msonline-outlook-95b76.templateTenant X-MS-Exchange-CrossTenant-Network-Message-Id: d5e866b0-5ecb-4050-99fe-08dcc184e156 X-MS-Exchange-CrossTenant-AuthSource: AM8P250MB0170.EURP250.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 21 Aug 2024 01:59:24.3290 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: PA2P250MB1069 From: bobwei9@hotmail.com (Bob Weinand) --------------bumRzmUSD3bwZBn3C8Uq67m1 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hey Arnauld, On 20.8.2024 15:01:28, Arnaud Le Blanc wrote: > Hi Bob, > > On Tue, Aug 20, 2024 at 12:18 AM Bob Weinand wrote: >> The fluid Arrays section says "A PoC has been implemented, but the performance impact is still uncertain". Where may I find that PoC for my curiosity? I'm imagining the implementation of the array types as a counted collection of types of the entries. But without the PoC I may only guess. > I may publish the PoC at some point, but in the meantime here is a > short description of how it's implemented: > > - The zend_array has a zend_type member representing the type of its elements > - Everytime we add or update a member, we union its type with the > array type. For simple types it's just a |= operation. For arrays with > a single class it's also simple. For complex types it's more expensive > currently, but it may be possible to cache transitions to make this > cheaper. > - Updating the array type on deletes requires to either maintain a > counter of every type, or to re-compute the type entirely everytime. > Both are probably too expensive. Instead, we don't update the type on > deletes, but we re-compute the type entirely when a type check fails. > This is based on two hypotheses: 1. A delete rarely changes an array's > type in practice, and 2. Type checks rarely fail That sounds like a clever way to do it. I like this approach. > - References are treated as mixed, so adding a reference to an array > or taking a reference to an element changes its type to mixed. Passing > an array to a more specific array will cause a > re-compute, which also de-refs every reference. Classifying a reference as mixed certainly makes this work and I guess it's probably an acceptable overhead. References into (big) arrays are not that common. Short of doing a foreach by-ref, but that's anyway an O(n) operation generally. > - Updating a nested element requires updating the type of every parent Does it actually? It just requires updating the type of the parent, if the own type is actually changed. But types of arrays don't change all the time, so that's likely an amortized constant time operation with respect to inserts/updates. >> It also says "Another issue is that [...] typed properties may not be possible.". Why would that be the case? Essentially a typed property would just be a static array, which you describe in the section right below. > It becomes complicated when arrays contain references or nested > arrays. Type constraints must be propagated to nested arrays, but also > removed when an array is not reachable via a typed property anymore. > > E.g. > > class C { > public array> $prop; > } > > $a = &$c->prop[0]; > $a[] = 'string'; // must be an error > unset($c->prop[0]); > $a[] = 'string'; // must be accepted In this case $a will decay from a RC=1 reference to a normal value. During the unreferencing operation the type restrictions can be dropped. That operation is only O(n) if it actually contains other references. > > $b = &$c->prop[1]; > $b[] = 'string'; // must be an error > $c->prop = []; > $a[] = 'string'; // must be accepted > > I don't remember all the possible cases, but I didn't find a way to > support this that didn't involve recursively scanning an array at some > point. IIRC, without references it's less of an issue, so a possible > way forward would be to forbid references to members of typed > properties. Unfortunately this breaks pass-by-reference, e.g. > `sort($c->prop)`. out/inout parameters may be part of a solution, but > with more array separations than pass-by-ref. Yes, you'll have to scan the array recursively, but only if it contains references (which you know thanks to array or array>). And you also only need to descend into arrays which contain references. If something contains a reference, you just slap a property type onto it - like "foreach entry in array { if entry is reference { add_type_source(inner type of entry) } }" - thus, in case of array>, you slap array onto it. This operation is only O(n) if the array type actually contains references (i.e. it will mismatch due to array, and you have to iterate anyway). So it will just work like references to property types do: these can also never violate the type containing them. At least in my mind. I'd also be happy to chat more about it off-list, but possibly easier too once the patch is public. > Best Regards, > Arnaud Overall I would not focus too much on making the case "reference into array" too much of a blocker. It should work, but it's fine if it comes with a couple rough edges regarding performance. I don't think arrays where you hold a reference into them are commonly passed around or big. There are a few edge cases like state machines built with array references, but the solution to these is ... don't type the property containing it then. And if it really becomes a problem, we may still invest time into it after landing it. Thanks, Bob --------------bumRzmUSD3bwZBn3C8Uq67m1 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit

Hey Arnauld,

On 20.8.2024 15:01:28, Arnaud Le Blanc wrote:
Hi Bob,

On Tue, Aug 20, 2024 at 12:18 AM Bob Weinand <bobwei9@hotmail.com> wrote:
The fluid Arrays section says "A PoC has been implemented, but the performance impact is still uncertain". Where may I find that PoC for my curiosity? I'm imagining the implementation of the array types as a counted collection of types of the entries. But without the PoC I may only guess.
I may publish the PoC at some point, but in the meantime here is a
short description of how it's implemented:

- The zend_array has a zend_type member representing the type of its elements
- Everytime we add or update a member, we union its type with the
array type. For simple types it's just a |= operation. For arrays with
a single class it's also simple. For complex types it's more expensive
currently, but it may be possible to cache transitions to make this
cheaper.
- Updating the array type on deletes requires to either maintain a
counter of every type, or to re-compute the type entirely everytime.
Both are probably too expensive. Instead, we don't update the type on
deletes, but we re-compute the type entirely when a type check fails.
This is based on two hypotheses: 1. A delete rarely changes an array's
type in practice, and 2. Type checks rarely fail

That sounds like a clever way to do it. I like this approach.

- References are treated as mixed, so adding a reference to an array
or taking a reference to an element changes its type to mixed. Passing
an array<mixed> to a more specific array<something> will cause a
re-compute, which also de-refs every reference.

Classifying a reference as mixed certainly makes this work and I guess it's probably an acceptable overhead. References into (big) arrays are not that common. Short of doing a foreach by-ref, but that's anyway an O(n) operation generally.

- Updating a nested element requires updating the type of every parent

Does it actually? It just requires updating the type of the parent, if the own type is actually changed. But types of arrays don't change all the time, so that's likely an amortized constant time operation with respect to inserts/updates.

It also says "Another issue is that [...] typed properties may not be possible.". Why would that be the case? Essentially a typed property would just be a static array, which you describe in the section right below.
It becomes complicated when arrays contain references or nested
arrays. Type constraints must be propagated to nested arrays, but also
removed when an array is not reachable via a typed property anymore.

E.g.

class C {
    public array<array<int>> $prop;
}

$a = &$c->prop[0];
$a[] = 'string'; // must be an error
unset($c->prop[0]);
$a[] = 'string'; // must be accepted
In this case $a will decay from a RC=1 reference to a normal value. During the unreferencing operation the type restrictions can be dropped. That operation is only O(n) if it actually contains other references.

$b = &$c->prop[1];
$b[] = 'string'; // must be an error
$c->prop = [];
$a[] = 'string'; // must be accepted

I don't remember all the possible cases, but I didn't find a way to
support this that didn't involve recursively scanning an array at some
point. IIRC, without references it's less of an issue, so a possible
way forward would be to forbid references to members of typed
properties. Unfortunately this breaks pass-by-reference, e.g.
`sort($c->prop)`. out/inout parameters may be part of a solution, but
with more array separations than pass-by-ref.

Yes, you'll have to scan the array recursively, but only if it contains references (which you know thanks to array<mixed> or array<array<mixed>>). And you also only need to descend into arrays which contain references.

If something contains a reference, you just slap a property type onto it - like "foreach entry in array { if entry is reference { add_type_source(inner type of entry) } }" - thus, in case of array<array<int>>, you slap array<int> onto it. This operation is only O(n) if the array type actually contains references (i.e. it will mismatch due to array<mixed>, and you have to iterate anyway).

So it will just work like references to property types do: these can also never violate the type containing them. At least in my mind.

I'd also be happy to chat more about it off-list, but possibly easier too once the patch is public.

Best Regards,
Arnaud


Overall I would not focus too much on making the case "reference into array" too much of a blocker. It should work, but it's fine if it comes with a couple rough edges regarding performance. I don't think arrays where you hold a reference into them are commonly passed around or big.

There are a few edge cases like state machines built with array references, but the solution to these is ... don't type the property containing it then. And if it really becomes a problem, we may still invest time into it after landing it.


Thanks,

Bob
--------------bumRzmUSD3bwZBn3C8Uq67m1--