Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project!

## [ clisp-Bugs-3451264 ] odd error with sort

 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-07 15:43:27 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by sds You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Pending Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Sam Steingold (sds) Date: 2011-12-07 07:43 Message: the bug is in you not understanding how lisp works. http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm sort and stable-sort destructively sort sequences according to the order determined by the predicate function. The sorting operation can be destructive in all cases. In the case of a vector argument, this is accomplished by permuting the elements in place. In the case of a list, the list is destructively reordered in the same manner as for nreverse. m is a symbol. its symbol-value slot contains a cons cell - the first cons cell in the list. after sorting that cons cell CAR and CDR contain different referents. if you want xsort to be non-destructive, you have to copy the argument: (defun xsort (x) (sort (copy-seq l) #'<)) ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:13 Message: Sorry but you example is wrong. See (setq l '(3 1 2)) (setq m l) (xsort l) m; this is completely stupid => (1 2 3) This is obviously a *nightmare* bug. :-( ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```

 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-05 11:40:20 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Tracker Item Submitted) made by litwr You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error Status: Open Resolution: None Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Bruno Haible (haible) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-05 16:43:26 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by sds You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Pending >Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) >Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-07 08:05:55 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by litwr You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Open Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-07 08:13:15 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by litwr You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error Status: Open Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:13 Message: Sorry but you example is wrong. See (setq l '(3 1 2)) (setq m l) (xsort l) m; this is completely stupid => (1 2 3) This is obviously a *nightmare* bug. :-( ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-07 15:43:27 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by sds You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Pending Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Sam Steingold (sds) Date: 2011-12-07 07:43 Message: the bug is in you not understanding how lisp works. http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm sort and stable-sort destructively sort sequences according to the order determined by the predicate function. The sorting operation can be destructive in all cases. In the case of a vector argument, this is accomplished by permuting the elements in place. In the case of a list, the list is destructively reordered in the same manner as for nreverse. m is a symbol. its symbol-value slot contains a cons cell - the first cons cell in the list. after sorting that cons cell CAR and CDR contain different referents. if you want xsort to be non-destructive, you have to copy the argument: (defun xsort (x) (sort (copy-seq l) #'<)) ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:13 Message: Sorry but you example is wrong. See (setq l '(3 1 2)) (setq m l) (xsort l) m; this is completely stupid => (1 2 3) This is obviously a *nightmare* bug. :-( ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-08 08:17:07 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Comment added) made by litwr You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Open Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- >Comment By: Vladimir Lidovski (litwr) Date: 2011-12-08 00:17 Message: Thank you very much! You should only give me this link at first. Yes, there is no any bug. Thank you again. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-07 07:43 Message: the bug is in you not understanding how lisp works. http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm sort and stable-sort destructively sort sequences according to the order determined by the predicate function. The sorting operation can be destructive in all cases. In the case of a vector argument, this is accomplished by permuting the elements in place. In the case of a list, the list is destructively reordered in the same manner as for nreverse. m is a symbol. its symbol-value slot contains a cons cell - the first cons cell in the list. after sorting that cons cell CAR and CDR contain different referents. if you want xsort to be non-destructive, you have to copy the argument: (defun xsort (x) (sort (copy-seq l) #'<)) ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:13 Message: Sorry but you example is wrong. See (setq l '(3 1 2)) (setq m l) (xsort l) m; this is completely stupid => (1 2 3) This is obviously a *nightmare* bug. :-( ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```
 [ clisp-Bugs-3451264 ] odd error with sort From: SourceForge.net - 2011-12-08 13:02:35 ```Bugs item #3451264, was opened at 2011-12-05 03:40 Message generated for change (Settings changed) made by sds You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: clisp Group: lisp error >Status: Pending Resolution: Invalid Priority: 5 Private: No Submitted By: Vladimir Lidovski (litwr) Assigned to: Sam Steingold (sds) Summary: odd error with sort Initial Comment: It is about GNU CLISP strange behaviours. The version 2.33/2.48/2.49 were tested. Can anybody explain this oddness? (defun xsort (l) (sort l #'<)) ;XSORT (setq l '(3 1 2)) ;(3 1 2) (xsort l) ;(1 2 3) l ; surprize! (1 2 3) The ordinal lisp function XSORT takes its argument by value but the presence of SORT-function in its body converts argument to passed by reference! Are there any reasonable explanation? BTW the POP-function doesn't make surprize: (defun xpop (l) (pop l)) ;XPOP (setq l '(3 1 2)) ;(3 1 2) (xpop l) ;3 l ;(3 2 1) ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-08 00:17 Message: Thank you very much! You should only give me this link at first. Yes, there is no any bug. Thank you again. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-07 07:43 Message: the bug is in you not understanding how lisp works. http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm sort and stable-sort destructively sort sequences according to the order determined by the predicate function. The sorting operation can be destructive in all cases. In the case of a vector argument, this is accomplished by permuting the elements in place. In the case of a list, the list is destructively reordered in the same manner as for nreverse. m is a symbol. its symbol-value slot contains a cons cell - the first cons cell in the list. after sorting that cons cell CAR and CDR contain different referents. if you want xsort to be non-destructive, you have to copy the argument: (defun xsort (x) (sort (copy-seq l) #'<)) ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:13 Message: Sorry but you example is wrong. See (setq l '(3 1 2)) (setq m l) (xsort l) m; this is completely stupid => (1 2 3) This is obviously a *nightmare* bug. :-( ---------------------------------------------------------------------- Comment By: Vladimir Lidovski (litwr) Date: 2011-12-07 00:05 Message: Thank you. These behaviours of SORT can produce nightmare side effects. The SORT is destructive. This is ok but XSORT is programmed as normal function! IMHO these odd things should be well documented. IMHO this is severe bug without proper documentation. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This is the way lisp works. sort is a destructive function, it modifies the list structure. other CL implementations use different modifications, so l might not be the sorted list. however, both behaviors are standard-compliant. try this: (setq m l) (setq s (xsort l)) (eq m l) ==> t, because xsort cannot modify l (eq s l) ==> t, because that's how clisp works pop is a macro, so it modifies the its argument's value. xpop is a function, so it cannot modify the value of l. ---------------------------------------------------------------------- Comment By: Sam Steingold (sds) Date: 2011-12-05 08:43 Message: This bug report is now marked as "pending"/"invalid". This means that we think that the problem you report is not a problem with CLISP. Unless you - the reporter - act within 2 weeks, the bug will be permanently closed. Sorry about the inconvenience - we hope your silence means that you agree that this is not a bug in CLISP. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=101355&aid=3451264&group_id=1355 ```