view trunk/mintl/array.d @ 1:5dd9f598bcd8

Update
author revcompgeek
date Sat, 29 Mar 2008 12:30:20 -0600
parents
children
line wrap: on
line source

/** \file array.d
 * \brief Utility functions for dynamic and associative arrays.
 * 
 * Written by Ben Hinkle and released to the public domain, as
 * explained at http://creativecommons.org/licenses/publicdomain
 * Email comments and bug reports to ben.hinkle@gmail.com
 *
 * revision 2.6
 */

module mintl.array;

// sort with custom compare delegate
template sort(Value:Value[]) {
  void sort(Value[] data, int delegate(Value* l, Value* r) cmp = null) {
    void swap( Value* t1, Value* t2 ) {
      Value t = *t1; *t1 = *t2; *t2 = t;
    }
    void insertionSort(Value[] data) {
      Value* head = &data[0];
      Value* tail = head+data.length;
      Value* i = head+1;
      while(i < tail) {
	Value* j = i;
	for (; j > head && cmp(j - 1,j) > 0; j--) {
	  swap(j - 1,j);
	}
	i++;
      }
    }
    void dosort(Value[] data) {
      if (data.length < 2) {
	return;
      } else if (data.length < 8) {
	insertionSort(data);
	return;
      }
      Value *head = &data[0];
      Value *tail = head+data.length-1;
      Value *p = head+1;
      Value *q = tail;
      swap(head,head+data.length/2);
      if (cmp(p,q) > 0) swap(p,q);
      if (cmp(head,q) > 0) swap(head,q);
      if (cmp(p,head) > 0) swap(p,head);
      while (1) {
	do p++; while (cmp(p, head) < 0);
	do q--; while (cmp(q, head) > 0);
	if (p > q) break;
	swap(p,q);
      }
      swap(head,q);
      if (head < q)
	dosort(head[0 .. q-head+1]);
      if (p < tail)
	dosort(p[0 .. tail-p+1]);
    }
    TypeInfo ti = typeid(Value);
    if (cmp is null) {
      cmp = cast(typeof(cmp))&ti.compare;
    }
    dosort(data);
  }
}

/** Reserve a capacity for a dynamic array. If the array already has
 * more elements or if the original length is zero it does nothing.
 * Compiler-dependent.
 * \param x the array to modify
 * \param n the requested capacity
 */
template reserve(Value : Value[]) {
  void reserve(inout Value[] x, size_t n) {
    size_t oldlen = x.length;
    if ((oldlen < n) && (oldlen > 0)) {
      x.length = n;
      x.length = oldlen;
    }
  }
}

/** Iterate backwards over a dynamic array. This function should be
 *  used on the target array in a foreach statement or
 *  or as the target to a call to toSeq <tt>x.backwards.toSeq</tt>
 *  \param x the array to iterate over.
 */
template backwards(Value : Value[]) {
  DArrayReverseIter!(Value) backwards(Value[] x) {
    DArrayReverseIter!(Value) y;
    y.x = x;
    return y;
  }
}

/* Private helper for reverse iteration */
private struct DArrayReverseIter(Value) {
  Value[] x;
  int opApply(int delegate(inout Value val) dg) {
    int res = 0;
    for (size_t n=x.length; n > 0; ) {
      res = dg(x[--n]);
      if (res) break;
    }
    return res;
  }
  int opApply(int delegate(inout size_t n, inout Value val) dg) {
    int res = 0;
    size_t cnt = 0;
    for (size_t n=x.length; n > 0; cnt++) {
      res = dg(cnt,x[--n]);
      if (res) break;
    }
    return res;
  }
}

//version = MinTLVerboseUnittest;
//version = MinTLUnittest;
version (MinTLUnittest) {
  private import std.random;
  unittest {
    version (MinTLVerboseUnittest) 
      printf("starting mintl.array unittest\n");

    int[] x;
    x.length = 1;
    reserve!(int[])(x,100);
    int[] y = x;
    x.length = 90;
    assert( cast(int*)x == cast(int*)y );
    version (MinTLVerboseUnittest) 
      printf("pass\n");

    int[] t1,t2;
    t1.length = 4;
    t2.length = 4;
    for(int k=0;k<4;k++) t1[k] = k*100;
    foreach(size_t n, int val; backwards!(int[])(t1)) {
      t2[n] = val;
    }
    assert( t1.reverse == t2 );
    version (MinTLVerboseUnittest) 
      printf("pass\n");

    double[int] c;
    c[100] = 1.1;
    c[300] = 2.2;
    c[-100] = 3.3;
    double v;
    assert( 100 in c );
    assert( !(200 in c) );
    assert( 300 in c );
    for (int k=0;k<1000;k++) {
      c[k*100] = 1;
    }

    // test simple sorting
    static int[] data = [40,300,-20,100,400,200];
    int[] s1 = data.dup;
    sort!(int[])(s1);
    static int[] s2 = [-20,40,100,200,300,400];
    assert( s1 == s2 );

    // test a large sort with default order
    double[] s3;
    for (int k=0;k<1000;k++) {
      s3 ~= 1.0*rand()/100000.0 - 500000.0;
    }
    double[] s4 = s3.dup;
    sort!(double[])(s3);
    for (int k=0;k<999;k++) {
      assert( s3[k] <= s3[k+1] );
    }
    // test a large sort with custom order
    int cmp(double*x,double*y){return *x>*y?-1:*x==*y?0:1;}
    sort!(double[])(s4,&cmp);
    for (int k=0;k<999;k++) {
      assert( s4[k] >= s4[k+1] );
    }

    version (MinTLVerboseUnittest) 
      printf("finished mintl.array unittest\n");
  }
}