bugGNU Octave - Bugs: bug #67096, nargout incorrect when index...

 
 

bug #67096: nargout incorrect when index expression with multiple outputs begins with function call

Submitter:  Fernando <tutissanalio>
Submitted:  Wed 07 May 2025 11:12:02 PM UTC
   
 
Category:  Interpreter Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Unexpected Error or Warning
Status:  Fixed Assigned to:  None
Originator Name:  Open/Closed:  * Closed
Release:  * bytecode-evaluator Release: 
Operating System:  * Any Fixed Release:  10.2.0
Planned Release:  10.2.0
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Sat 12 Jul 2025 07:24:06 PM UTC, comment #30: 

Fixed on stable and bytecode-interpreter branches.  Closing report.

Rik <rik5>
Group administrator
Thu 10 Jul 2025 01:30:20 PM UTC, comment #29: 

Thanks Fernando. With that changeset, make check now gives zero failures:


  PASS                            19994
  FAIL                                0
  XFAIL (reported bug)               36
  SKIP (missing feature)             57
  SKIP (run-time condition)          34


I made a couple of very minor changes (merged the declaration and definition of bool last, and adjusted spacing around a conditional operator), and pushed it to rev 67e19a2175ab.

Marking as ready for test.

Arun Giridhar <arungiridhar>
Group Member
Thu 10 Jul 2025 09:26:47 AM UTC, comment #28: 

Sorry, I attached a diff file but my intention was to attach a changeset file with commit message. Here it goes.

(file #57392)

Fernando <tutissanalio>
Thu 10 Jul 2025 06:29:09 AM UTC, comment #27: 

I think I have it. See proposed patch.

As pointed out in comment #7, the problem happens when the index expression is compiled. So for example:


[a, b] = __fcell () {1:2}


works correctly if executed from the command window, but fails if it is inside a (compiled) function. When running the tests, it fails because the code of each test is converted to a function before being run.

For a function like this:

function myindex
  [a,b]=__fcell(){1:2}
end


you get the following bytecode:

octave:5> __vm_print_bytecode__ myindex
metadata:
        myindex
        user-function

frame:
        .n_return 3
        .n_args 0
        .n_locals 6

slots:
    0: %nargout
    1: a
    2: b
    3: ans
    4: __fcell
    5: myindex

source code lut:
        l:    2 c:    7 ip0:    4 ip1:   32
        l:    2 c:   15 ip0:    4 ip1:   26
        l:    2 c:   15 ip0:    4 ip1:    6
        l:    3 c:    5 ip0:   32 ip1:   33
dbg tree object:
        ip:4 obj=0x79a464da8a00
        ip:32 obj=0x79a4642aac80
code: (n=34)
            4: PUSH_SLOT_INDEXED 4 # __fcell
            6: INDEX_STRUCT_CALL 2 4 0 '(' # __fcell
           12: INDEX_STRUCT_SUBCALL 2 0 2 0 '('
           18: LOAD_CST 3
           20: INDEX_STRUCT_SUBCALL 2 1 2 1 '{'
           26: ASSIGNN 2 1 2 # a b
           32: RET
           33: RET


The important part is the chain of index operations: INDEX_STRUCT_CALL followed by a series of INDEX_STRUCT_SUBCALL. The first argument of each of these operations is the number of output arguments. It is 2, but should be 1 for all index operations except for the last one.

The proposed change modifies the compilation to do just that. So the code for the index operations will be:

            6: INDEX_STRUCT_CALL 1 4 0 '(' # __fcell
           12: INDEX_STRUCT_SUBCALL 1 0 2 0 '('
           18: LOAD_CST 3
           20: INDEX_STRUCT_SUBCALL 2 1 2 1 '{'


(file #57390)

Fernando <tutissanalio>
Mon 30 Jun 2025 05:58:57 PM UTC, comment #26: 

I have narrowed it down to the way that root_nargout is computed when calling this function:


  octave_value_list
  vm::execute_code (const octave_value_list &root_args, int root_nargout)


  When the following code is copy-pasted at the command prompt:


  function [a] = __fcell ()
    a = num2cell (1:5);
  endfunction

  [a, b] = __fcell {1:2}


then these are the internal variables:

  root_nargout = 1
  n_returns = 2
  n_args = 0
  n_locals = 5
  is_varargin = 0
  is_varargout = 0
  n_root_args = 0

  a = 1
  b = 2


When the same code is called as part of `make check`, then these are the internal variables:

  Calling __fcell #1

  root_nargout = 2
  n_returns = 2
  n_args = 0
  n_locals = 5
  is_varargin = 0
  is_varargout = 0
  n_root_args = 0


The only difference is that root_nargout has become 2 instead of 1, causing this check to cause an error:


if (!is_varargout && root_nargout > n_returns - 1) // n_returns includes %nargout, so subtract one
  {
    std::cout << __FILE__ << ':' << __LINE__ << '\n';
    std::string fn_name = unwind_data->m_name;
    (*sp++).pee = new execution_exception {"error", "Octave:invalid-fun-call",
                                           fn_name + ": function called with too many outputs"};
    (*sp++).i = static_cast<int> (error_type::EXECUTION_EXC);
    ip++;
    goto unwind;
  }


The function vm::execute_code is defined around line 990 in pt-bytecode-vm.cc and called around line 8620 in the same file, from this function:

  octave_value_list
  vm::call (tree_evaluator& tw, int nargout, const octave_value_list& xargs,
            octave_user_code *fn, std::shared_ptr<stack_frame> context)


So the value of nargout is being passed incorrectly to that function in the test environment. Not sure why yet, but it seems to only affect the test environment for some reason.

Arun Giridhar <arungiridhar>
Group Member
Mon 30 Jun 2025 04:05:39 PM UTC, comment #25: 

Adding to comment #24:

I tried that code because I was alarmed that indexing etc wouldn't reliably work anymore with the bytecode-interpreter.
But now I'm a little less worried.
FWIW, up till now all the code I used for my previous and current projects still works fine, with or w/o enabled bytecode-interpreter. Keeping fingers crossed ...

Philip Nienhuis <philipnienhuis>
Group Member
Mon 30 Jun 2025 04:01:11 PM UTC, comment #24: 

Just experimented a little bit (I really should do other stuff but I couldn't resist).
Looks like it has to do with some initialization in the bytecode-interpreter.

Defining __fcell as a command line function helps a bit (see also comment #6):

>> function [a] = __fcell ()
  a = num2cell (1:5);
endfunction
>> which __fcell
'__fcell' is a function from the file C:/Users/philip/AppData/Local/Temp/octave_qwUOpU.m
>> [a, b] = __fcell () {1:2}
error: __fcell: function called with too many outputs
>> __fcell()
ans =
{
  [1,1] = 1
  [1,2] = 2
  [1,3] = 3
  [1,4] = 4
  [1,5] = 5
}

>> __fcell(){1:2}
ans = 1
ans = 2
## same stmt failing earlier on:
>> [a, b] = __fcell() {1:2}
a = 1
b = 2

so it also looks like __fcell must have run first (? = has to be compiled by the VM?) before it works "properly".  Does the VM recognize the command line function in the tests?

Maybe this provides a clue for fixing this bytecode-interpreter bug.

Philip Nienhuis <philipnienhuis>
Group Member
Mon 30 Jun 2025 03:33:32 PM UTC, comment #23: 

Comment #5 refers to two failures in the BISTs from early May. The additional push to the default branch in comment #18 is causing five additional failures listed in comment #21.

All seven BIST failures look like they have the same cause?

Reopening as In Progress for the bytecode-interpreter branch alone. (The stable and default branches are already fixed.)

I am inclined to keeping the discussion in this place instead of creating a new one, to help with the context.

Arun Giridhar <arungiridhar>
Group Member
Mon 30 Jun 2025 02:20:45 PM UTC, comment #22: 

@Arun, It seems it was pointed out in comment #5, but there have been one or two more patches since then.

Should we reopen this report until it is fixed for bytecode-interpreter? Or open a separate bug-report for that?

Fernando <tutissanalio>
Mon 30 Jun 2025 12:47:50 PM UTC, comment #21: 

I thought this following problem was already reported some 2-3 weeks ago but I can't locate the report, so here it is (possibly again):

This changeset broke BISTs in the bytecode-interpreter branch. The following errors occur during `make check`:


>>>>> processing ...../libinterp/parse-tree/pt-idx.cc-tst
***** test  # Function call at the start of the index expression
 [a, b] = __fcell () {1:2};
 assert ([a, b], [1, 2]);
 [a, b] = __fstruct ().b;
 assert ([a, b], [3, 4]);
!!!!! test failed
__fcell: function called with too many outputs
***** test  # Function handle call at the start of the index expression
 f = @__fcell;
 [a, b] = f () {1:2};
 assert ([a, b], [1, 2]);
 f = @__fstruct;
 [a, b] = f ().b;
 assert ([a, b], [3, 4]);
!!!!! test failed
__fcell: function called with too many outputs
***** test  # Intermediate function handle call followed by {} indexing
 x = {@__fcell};
 [a, b] = x {1} () {1:2};
 assert ([a, b], [1, 2]);
 s.f = @__fcell;
 [a, b] = s.f () {3:4};
 assert ([a, b], [3, 4]);

!!!!! test failed
__fcell: function called with too many outputs
***** test  # Intermediate function handle call followed by . indexing
 x = {@__fstruct};
 [a, b] = x {1} ().a;
 assert ([a, b], [1, 2]);
 s.f = @__fstruct;
 [a, b] = s.f ().b;
 assert ([a, b], [3, 4]);

!!!!! test failed
__fstruct: function called with too many outputs
***** test  # Anonymous function call at the start of the index expression
 f = @() num2cell (1:5);
 [a, b] = f () {1:2};
 assert ([a, b], [1, 2]);
 f = @() struct ('a', {1, 2}, 'b', {3, 4});
 [a, b] = f ().b;
 assert ([a, b], [3, 4]);
!!!!! test failed
invalid undefined value in chained index expression
***** test  # Intermediate anon func call followed by {} indexing
 x = {@() num2cell (1:5)};
 [a, b] = x {1} () {1:2};
 assert ([a, b], [1, 2]);
 s.f = @() num2cell (1:5);
 [a, b] = s.f () {3:4};
 assert ([a, b], [3, 4]);

!!!!! test failed
invalid undefined value in chained index expression
***** test  # Intermediate anon func call followed by . indexing
 x = {@() struct ('a', {1, 2}, 'b', {3, 4})};
 [a, b] = x {1} ().a;
 assert ([a, b], [1, 2]);
 s.f = @() struct ('a', {1, 2}, 'b', {3, 4});
 [a, b] = s.f ().b;
 assert ([a, b], [3, 4]);
!!!!! test failed
invalid undefined value in chained index expression


Arun Giridhar <arungiridhar>
Group Member
Thu 12 Jun 2025 05:46:31 PM UTC, comment #20: 

The original bug report was more specific about "Fix nargout in multi-output index expressions beginning with function call" and that first patch was applied to stable.  Hence, the Fixed Release was 10.2.0.  There were further issues fixed in subsequent changesets, but those went on the default branch and will be part of 11.1.0 release.

Rik <rik5>
Group administrator
Wed 11 Jun 2025 11:09:05 AM UTC, comment #19: 

It says "Fixed Release: 10.2.0". Is that right? 10.2.0 was released before the final changeset https://hg.savannah.gnu.org/hgweb/octave/rev/0c13ece26367 . Also, that changeset was done in the default branch, not stable.

Fernando <tutissanalio>
Mon 09 Jun 2025 05:49:37 PM UTC, comment #18: 

Back from vacation and checked in your final changeset here https://hg.savannah.gnu.org/hgweb/octave/rev/0c13ece26367.

Marking bug as Fixed and closing report.

Rik <rik5>
Group administrator
Sun 01 Jun 2025 10:37:23 PM UTC, comment #17: 

I had a look at the issue from comment #13 and updated the latest patch (from comment #15). I attach it here.

(file #57267)

Fernando <tutissanalio>
Fri 30 May 2025 07:50:33 AM UTC, comment #16: 

Sorry it that was not clear. No, the patch in comment #14 does not address the issue identified in comment #13. I would like to have a look at that issue and propose a patch (or update the current patch).

Fernando <tutissanalio>
Thu 29 May 2025 09:38:24 PM UTC, comment #15: 

I downloaded and compiled the latest patch, but it doesn't seem to address the issue identified in comment #13.  Is it supposed to?  Maybe the patch in comment #14 is for other purposes.

In any case, I modified the patch to use more Octave coding conventions.  It is attached.  I also attach the SimpleClass.m file.  The motivating example, for Matlab compatibility, is


sc=SimpleClass;
[a,b]=sc.get.data





(file #57254, file #57255)

Rik <rik5>
Group administrator
Fri 16 May 2025 02:51:23 PM UTC, comment #14: 

I realized I had completely messed up the commit message for the patch in comment #12. I resend it here fixing the commit message. Also, I set nargout to 1 instead of -1. It is not necessary to use -1 here, since all non-final subsref expressions (and function calls) should return exactly one result.

I also checked that this does not affect non-final subsref operations with . or {}, i.e. if they produce more than one output (a cs-list), an error is thrown, just like without the patch, e.g.

octave:> x=struct('a',{num2cell(1:5),num2cell(2:5),num2cell(3:5)});
octave:> r=x.a{1}
error: a cs-list cannot be further indexed
octave:> y={struct('a',10),struct('a',20),struct('a',30)};
octave:> r=y{:}.a
error: a cs-list cannot be further indexed


So this should only affect function calls.

(file #57228)

Fernando <tutissanalio>
Thu 15 May 2025 07:19:39 PM UTC, comment #13: 

Maybe we can accept the original patch and open a new report for the issue in comment #12. I have also seen anoother case that fails, with or without the second patch:


classdef SimpleClass
   methods
      function r = get(obj)
         r = struct('data',num2cell(nargout:nargout+4));
      end
   end
end

octave:> sc=SimpleClass;
octave:> sc.get.data
ans = 0
ans = 1
ans = 2
ans = 3
ans = 4
octave:> [a,b]=sc.get.data
error: get: function called with too many outputs
error: called from
    get


It works if you put the () in the method call:

octave:> [a,b]=sc.get().data
a = 1
b = 2


In Matlab (R2024b), it works with or without the ().

Fernando <tutissanalio>
Tue 13 May 2025 05:13:17 PM UTC, comment #12: 

I did not take into account that there can be also a function call at an intermediate position of the index expression (via function handle, anonymous function, something else?).

I rewrote the patch (with tests) to consider that case too. Maybe this report's summary could be updated.

(file #57221)

Fernando <tutissanalio>
Tue 13 May 2025 09:50:36 AM UTC, comment #11: 


> The code for the current bytecode interpreter is only present on a separate branch.  It is not part of the default development branch of Octave.  So it is definitely not enabled by default on the branch that is currently on track to become Octave 11.x

Yes I forgot, thanks for pointing that out :-)

> If you are using it then you must have explicitly switched to the bytecode-interpreter branch at some point in the past.

Yes.
I'm blindly assuming that I do get "default branch behavior" after entering the command '__vm_enable__ (0)' , but I don't know if that were always completely true. FWIW, up till now I haven't noted any differences.

Philip Nienhuis <philipnienhuis>
Group Member
Tue 13 May 2025 03:48:29 AM UTC, comment #10: 

RE: Comment #8:  The code for the current bytecode interpreter is only present on a separate branch.  It is not part of the default development branch of Octave.  So it is definitely not enabled by default on the branch that is currently on track to become Octave 11.x.  If you are using it then you must have explicitly switched to the bytecode-interpreter branch at some point in the past.

John W. Eaton <jwe>
Group administrator
Mon 12 May 2025 08:07:47 PM UTC, comment #9: 

comentario nº8:

> OK, so what do we do about it?


Is it an option to just ignore these 2 BIST errors for now, until someone fixed the issue in the bytecode-interpreter? After all, the error has been there up to now, only we did not have BIST tests to detect it.

> AFAICS the problematic index expression isn't inside the command line function used for the BISTs. Yet somehow the bytecode-interpreter stumbles over it.


I think the test function get the BIST code and wraps it into a function before running it.

Fernando <tutissanalio>
Mon 12 May 2025 07:03:21 PM UTC, comment #8: 

OK, so what do we do about it?

Currently it's only a problem with dev Octave (11.0.0) as the bytecode-interpreter is enabled by default there.
On stable the bytecode-interpreter isn't implemented so this is no problem for soon-to-come 10.2.0.

AFAICS the problematic index expression isn't inside the command line function used for the BISTs. Yet somehow the bytecode-interpreter stumbles over it.
In the course of an earlier bug report I was wondering if there could be a config feature setting for the bytecode-interpreter so that BISTs can be skipped; but the issue with that is that the fault lies in the bytecode-interpreter rather than in the BIST. So that would be a wrong solution.

(FYI I mostly run bleeding edge Octave + pkgs for my projects so I'm often one of the first to hit my nose on s/th.)

Philip Nienhuis <philipnienhuis>
Group Member
Mon 12 May 2025 11:13:32 AM UTC, comment #7: 

It seems that the failure with the bytecode-interpreter happens when the problematic index expression is in a compiled function.


# Function defined in file or in command-line
function [a]=fcell()
  a=num2cell(1:2);
endfunction

# Function defined in file or in command-line
function [a,b]=myindex()
 [a, b] = fcell () {1:2};
end

octave:> [a, b] = fcell () {1:2}
a = 1
b = 2

octave:> myindex()
error: fcell: function called with too many outputs


Fernando <tutissanalio>
Sun 11 May 2025 05:49:32 PM UTC, comment #6: 

Seems this has more to do with the way the bytecode-interpreter handles functions declared in tests. If I declare fcell() before the test, it runs fine:

## just running test
>> test pt-idx.cc-tst
***** test
 function [a] = fcell ()
   a = num2cell (1:2);
 endfunction

 [a, b] = fcell () {1:2};
 assert ([a, b], [1, 2])
!!!!! test failed
fcell: function called with too many outputs

## declaring function on command line
>>  function [a] = fcell ()
   a = num2cell (1:2);
 endfunction

>> [a, b] = fcell ()
error: fcell: function called with too many outputs

## now test runs fine
>> [a, b] = fcell () {1:2}
a = 1
b = 2
>> fcell
ans =
{
  [1,1] = 1
  [1,2] = 2
}

>> __vm_enable__ ()
ans = 1


Philip Nienhuis <philipnienhuis>
Group Member
Sun 11 May 2025 12:32:16 PM UTC, comment #5: 

On dev Octave I get 2 FAILs for the new BISTs. Excerpt from fntests.log:

:
>>>>> processing /home/philip/devel/octdev/oct1100+/libinterp/parse-tree/pt-idx.cc-tst
***** test
 function [a] = fcell ()
   a = num2cell (1:2);
 endfunction

 [a, b] = fcell () {1:2};
 assert ([a, b], [1, 2])
!!!!! test failed
fcell: function called with too many outputs
***** test
 function [a] = fstruct ()
   a = struct ('a', {1, 2}, 'b', {3, 4});
 endfunction

 [a, b] = fstruct ().b;
 assert ([a, b], [3, 4])
!!!!! test failed
fstruct: function called with too many outputs
:

But that was with the bytecode-interpreter active. See:

## cd to libinterp/parse-tree/
>> test pt-idx.cc-tst
***** test
 function [a] = fcell ()
   a = num2cell (1:2);
 endfunction

 [a, b] = fcell () {1:2};
 assert ([a, b], [1, 2])
!!!!! test failed
fcell: function called with too many outputs
>>
>> __vm_enable__ ()
ans = 1
>>
>> __vm_enable__ (0)
>> clear -f
>>
>> test pt-idx.cc-tst
PASSES 8 out of 8 tests
>>


Philip Nienhuis <philipnienhuis>
Group Member
Thu 08 May 2025 03:36:50 PM UTC, comment #4: 

These changes look pretty safe to me. And they are fixing a bug. So, I went ahead and pushed the patch (with only minor changes) to the stable branch:
https://hg.savannah.gnu.org/hgweb/octave/rev/42c080f27d71

It should be part of Octave 10.2.0 when it is released.

Marking as ready for test.

Markus Mützel <mmuetzel>
Group administrator
Thu 08 May 2025 06:59:35 AM UTC, comment #3: 

Yes, I figured that out after posting my comment, I am afraid.

Andreas Bertsatos <pr0m1th3as>
Thu 08 May 2025 06:19:46 AM UTC, comment #2: 

Thank you again for your contributions. Your patch looks good to me (untested so far).

Just some minor style issues (that I can also address locally when I come around to testing this):

  • Whitespace between function name and opening parenthesis in line 26 of your patch.
  • " (bug #67096)." at the end of the first line of the commit message.
  • Start sentences with capital letter in commit message.


(@Andreas: That's not the point of this report. It is about the indexing immediately after the function call...)

Markus Mützel <mmuetzel>
Group administrator
Thu 08 May 2025 05:27:16 AM UTC, comment #1: 

The way you define your function is with a single variable output. If you want multiple variable output arguments, then define

function varargout = fcell ()
  varargout = num2cell (1:2);
endfunction


Andreas Bertsatos <pr0m1th3as>
Wed 07 May 2025 11:12:02 PM UTC, original submission:  

If I define this function

function [a] = fcell ()
  a = num2cell (1:2);
endfunction

I get an error in the following sentence (which should result in a=1, b=2).

octave:> [a,b] = fcell () {1:2}
error: fcell: function called with too many outputs
error: called from
    fcell

The same with this:

function [a] = fstruct ()
  a = struct ('a', {1, 2}, 'b', {3, 4});
end
octave:> [a,b] = fstruct ().a
error: fstruct: function called with too many outputs
error: called from
    fstruc

The problem is that [a,b] is taken as the outpus of the function call, when they are the outputs of indexing with {} or . The solution seems easy. Here is a possible fix, with a couple of tests.

Fernando <tutissanalio>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

Attached Files
file #57392:  bug67096-bytecode.cset added by tutissanalio (3KiB - application/octet-stream)
file #57267:  bug67096-2.cset added by tutissanalio (7KiB - application/octet-stream)
file #57254:  bug67096.cset added by rik5 (5KiB - application/octet-stream)
file #57255:  SimpleClass.m added by rik5 (138B - text/x-octave)
file #57228:  bug67096.patch added by tutissanalio (4KiB - text/x-patch)
file #57221:  bug67096.patch added by tutissanalio (4KiB - text/x-patch)
file #57200:  bug.patch added by tutissanalio (2KiB - text/x-patch)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by arungiridhar (Posted a comment)
  • -email is unavailable- added by rik5 (Updated the item)
  • -email is unavailable- added by jwe (Posted a comment)
  • -email is unavailable- added by philipnienhuis (Posted a comment)
  • -email is unavailable- added by mmuetzel (Posted a comment)
  • -email is unavailable- added by pr0m1th3as (Posted a comment)
  • -email is unavailable- added by tutissanalio (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only group members can vote.

     

    Follow 21 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    19:24 rik5 StatusReady For Test Fixed
        Open/ClosedOpen Closed
    2025-07-10 arungiridhar StatusIn Progress Ready For Test
    2025-07-10 tutissanalio Attached File- Added bug67096-bytecode.cset, #57392
    2025-07-10 tutissanalio Attached File- Added bug67096-bytecode.diff, #57390
    2025-06-30 arungiridhar StatusFixed In Progress
        Open/ClosedClosed Open
        Releasedev bytecode-evaluator
    2025-06-09 rik5 StatusReady For Test Fixed
        Open/ClosedOpen Closed
        Fixed ReleaseNone 10.2.0
    2025-06-01 tutissanalio Attached File- Added bug67096-2.cset, #57267
    2025-05-29 rik5 Attached File- Added bug67096.cset, #57254
        Attached File- Added SimpleClass.m, #57255
    2025-05-16 tutissanalio Attached File- Added bug67096.patch, #57228
    2025-05-13 tutissanalio Attached File- Added bug67096.patch, #57221
    2025-05-08 mmuetzel StatusPatch Reviewed Ready For Test
        Planned Release11.1.0 (current default) 10.2.0
    2025-05-08 mmuetzel StatusNone Patch Reviewed
        Planned ReleaseNone 11.1.0 (current default)
    2025-05-07 tutissanalio Attached File- Added bug.patch, #57200

    Back to the top

    Powered by Savane 3.15-e6e5.
    Corresponding source code