*! version 1.0.0  25sep2025


version 10


mata :


mata set matastrict   on
mata set mataoptimize on


struct tuples__info
{
    // input

    pointer(transmorphic vector) scalar v
    real                         scalar n
    pointer(real vector)         scalar r
    real                         scalar nr

    // Algorithm AS 88 nCr (Gentleman, 1975)

    real                         scalar ri
    real                         scalar i
    real                         scalar kount
    real                         scalar nmr
    real                         vector j

    // ancillary

    real                         scalar kount_missing
    real                         scalar ncomb
    real                         scalar nmiss
    transmorphic                 matrix empty_tuple
    transmorphic                 matrix missing_tuple
}


    /*  _____________________________________________________________________
                                                            tuplessetup()  */

transmorphic scalar tuplessetup(

    transmorphic    vector v,
  | real            vector r

    )
{
    struct tuples__info scalar t


    t.v             = &v
    t.n             = length(*t.v)

    t.r             = length(r) ? &r : &(1..t.n)
    t.nr            = length(*t.r)

    t.ri            = 0
    t.i             = 0

    t.kount         = 0
    t.kount_missing = 0

    t.empty_tuple   = J((rows(*t.v)==1),(rows(*t.v)!=1),missingof(*t.v))
    t.missing_tuple = J(0,0,missingof(*t.v))

    return(t)
}

transmorphic scalar tuplesinit(transmorphic vector v, | real vector r)
{
    return(args()<2 ? tuplessetup(v) : tuplessetup(v,r))
}


    /*  _____________________________________________________________________
                                                              tuplesget()  */

transmorphic matrix tuplesget(struct tuples__info scalar t)
{
    transmorphic vector res


    if ( tuplesdone(t) )
        return(t.empty_tuple)

    if ( !t.i ) {

        (void) t.ri++

        if ( ((*t.r)[t.ri]<1) | ((*t.r)[t.ri]>t.n) ) {

            (void) t.kount_missing++

            return(t.missing_tuple)

        }

        t.nmr = t.n-(*t.r)[t.ri]
        t.j   = ((t.i=1)..(*t.r)[t.ri])

    }

    (void) t.kount++

    res = (*t.v)[t.j]

    t.i = (*t.r)[t.ri]

    while (t.j[t.i] >= t.nmr+t.i) if ( !(--t.i) ) break

    if ( t.i ) {

        t.j[t.i] = t.j[t.i] + 1

        while (t.i < (*t.r)[t.ri]) {

            t.j[t.i+1] = t.j[t.i] + 1
            (void) t.i++

        }

    }

    return(res)
}


   /*  _____________________________________________________________________
                                                            tuplesdone()  */


real scalar tuplesdone(struct tuples__info scalar t)
{
    if ( t.i )
        return(0)

    if (t.ri+1 > t.nr)
        return(1)

    return( !t.n )
}


   /*  _____________________________________________________________________
                                        tupleskount(), tupleskountrest()  */

real scalar tupleskount(

    struct tuples__info scalar t,
  | real                scalar all

    )
{
    return((all ? t.kount + t.kount_missing : t.kount))
}

real scalar tuplescount(struct tuples__info scalar t, | real scalar all)
{
    return(tupleskount(t,all))
}


real scalar tupleskountrest(

    struct tuples__info scalar t,
  | real                scalar all

    )
{
    if (t.ncomb == .)
        t.ncomb = rowsum(comb(t.n,editvalue(*t.r,0,.)))

     if ( !all )
        return(t.ncomb - t.kount)

     if (t.nmiss == .)
        t.nmiss = t.n ? rowsum((*t.r:<1):|(*t.r:>t.n)) : 0

     return((t.ncomb+t.nmiss) - (t.kount+t.kount_missing))
}

real scalar tuplescountrest(struct tuples__info scalar t, | real scalar all)
{
    return(tupleskountrest(t,all))
}


   /*  _____________________________________________________________________
                                    tuplessetempty(), tuplessetmissing()  */

transmorphic matrix tuplessetempty(

    struct tuples__info scalar t,
  | transmorphic        matrix value

    )
{
    if (args() > 1)
        t.empty_tuple = value

    return(t.empty_tuple)
}

transmorphic matrix tuplessetmissing(

    struct tuples__info scalar t,
  | transmorphic        matrix value

    )
{
    if (args() > 1)
        t.missing_tuple = value

    return(t.missing_tuple)
}


end