github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/table/sort.go (about)

     1  package table
     2  
     3  import (
     4  	"github.com/hirochachacha/plua/internal/arith"
     5  	"github.com/hirochachacha/plua/internal/limits"
     6  	"github.com/hirochachacha/plua/object"
     7  	"github.com/hirochachacha/plua/object/fnutil"
     8  )
     9  
    10  func sort(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    11  	ap := fnutil.NewArgParser(th, args)
    12  
    13  	t, err := ap.ToTable(0)
    14  	if err != nil {
    15  		return nil, err
    16  	}
    17  
    18  	var lt object.Value
    19  
    20  	if len(args) > 1 {
    21  		lt, err = ap.ToFunctionOrNil(1)
    22  		if err != nil {
    23  			return nil, err
    24  		}
    25  	}
    26  
    27  	tlen, err := callLen(th, t)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	if tlen >= int(limits.MaxInt) {
    33  		return nil, ap.ArgError(0, "array too big")
    34  	}
    35  
    36  	err = quickSort(th, t, lt, 1, tlen)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	return nil, nil
    42  }
    43  
    44  func quickSort(th object.Thread, t object.Table, lt object.Value, lo, hi int) *object.RuntimeError {
    45  	if lo < hi {
    46  		p, err := partition(th, t, lt, lo, hi)
    47  		if err != nil {
    48  			return err
    49  		}
    50  		err = quickSort(th, t, lt, lo, p)
    51  		if err != nil {
    52  			return err
    53  		}
    54  		err = quickSort(th, t, lt, p+1, hi)
    55  		if err != nil {
    56  			return err
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  func partition(th object.Thread, t object.Table, lt object.Value, lo, hi int) (int, *object.RuntimeError) {
    63  	pivot, err := arith.CallGettable(th, t, object.Integer(lo))
    64  	if err != nil {
    65  		return 0, err
    66  	}
    67  
    68  	i := lo - 1
    69  	j := hi + 1
    70  
    71  	var xi, xj object.Value
    72  
    73  	for {
    74  		for {
    75  			i++
    76  			xi, err = arith.CallGettable(th, t, object.Integer(i))
    77  			if err != nil {
    78  				return 0, err
    79  			}
    80  			b, err := callLessThan(th, lt, xi, pivot)
    81  			if err != nil {
    82  				return 0, err
    83  			}
    84  			if !b {
    85  				break
    86  			}
    87  			if i == hi {
    88  				return 0, object.NewRuntimeError("invalid order function for sorting")
    89  			}
    90  		}
    91  		for {
    92  			j--
    93  			xj, err = arith.CallGettable(th, t, object.Integer(j))
    94  			if err != nil {
    95  				return 0, err
    96  			}
    97  			b, err := callLessThan(th, lt, pivot, xj)
    98  			if err != nil {
    99  				return 0, err
   100  			}
   101  			if !b {
   102  				break
   103  			}
   104  			if j < i {
   105  				return 0, object.NewRuntimeError("invalid order function for sorting")
   106  			}
   107  		}
   108  		if i >= j {
   109  			return j, nil
   110  		}
   111  		err = tabSwap(th, t, i, j)
   112  		if err != nil {
   113  			return 0, err
   114  		}
   115  	}
   116  }
   117  
   118  func tabSwap(th object.Thread, t object.Table, i, j int) *object.RuntimeError {
   119  	x, err := arith.CallGettable(th, t, object.Integer(i))
   120  	if err != nil {
   121  		return err
   122  	}
   123  	y, err := arith.CallGettable(th, t, object.Integer(j))
   124  	if err != nil {
   125  		return err
   126  	}
   127  	err = arith.CallSettable(th, t, object.Integer(j), x)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	err = arith.CallSettable(th, t, object.Integer(i), y)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  
   138  func callLessThan(th object.Thread, lt, x, y object.Value) (bool, *object.RuntimeError) {
   139  	if lt == nil {
   140  		b, err := arith.CallLessThan(th, false, x, y)
   141  		if err != nil {
   142  			return false, err
   143  		}
   144  
   145  		return b, nil
   146  	}
   147  
   148  	rets, err := th.Call(lt, x, y)
   149  	if err != nil {
   150  		return false, err
   151  	}
   152  	if len(rets) == 0 {
   153  		return false, nil
   154  	}
   155  	return object.ToGoBool(rets[0]), nil
   156  }