Mercurial > hg > octave-lyh
comparison liboctave/oct-sort.cc @ 9362:2ebf3ca62add
use a smarter algorithm for default lookup
author | Jaroslav Hajek <highegg@gmail.com> |
---|---|
date | Thu, 18 Jun 2009 15:06:35 +0200 |
parents | 9fd5c56ce57a |
children | da465c405d84 |
comparison
equal
deleted
inserted
replaced
9361:a00e219c402d | 9362:2ebf3ca62add |
---|---|
1740 retval = is_sorted_rows (data, rows, cols, compare); | 1740 retval = is_sorted_rows (data, rows, cols, compare); |
1741 | 1741 |
1742 return retval; | 1742 return retval; |
1743 } | 1743 } |
1744 | 1744 |
1745 // A helper function (just to avoid repeating expressions). | |
1746 template <class T, class Comp> | |
1747 inline octave_idx_type | |
1748 lookup_impl (const T *data, octave_idx_type lo, | |
1749 octave_idx_type hi, const T& val, Comp comp) | |
1750 { | |
1751 while (lo < hi) | |
1752 { | |
1753 octave_idx_type mid = lo + ((hi-lo) >> 1); | |
1754 if (comp (val, data[mid])) | |
1755 hi = mid; | |
1756 else | |
1757 lo = mid + 1; | |
1758 } | |
1759 | |
1760 return lo; | |
1761 } | |
1762 | |
1745 | 1763 |
1746 template <class T> template <class Comp> | 1764 template <class T> template <class Comp> |
1747 octave_idx_type | 1765 octave_idx_type |
1748 octave_sort<T>::lookup (const T *data, octave_idx_type nel, | 1766 octave_sort<T>::lookup (const T *data, octave_idx_type nel, |
1749 const T& value, Comp comp) | 1767 const T& value, Comp comp) |
1750 { | 1768 { |
1751 return std::upper_bound (data, data + nel, value, comp) - data; | 1769 return lookup_impl (data, 0, nel, value, comp); |
1752 } | 1770 } |
1753 | 1771 |
1754 template <class T> | 1772 template <class T> |
1755 octave_idx_type | 1773 octave_idx_type |
1756 octave_sort<T>::lookup (const T *data, octave_idx_type nel, | 1774 octave_sort<T>::lookup (const T *data, octave_idx_type nel, |
1772 retval = lookup (data, nel, value, std::ptr_fun (compare)); | 1790 retval = lookup (data, nel, value, std::ptr_fun (compare)); |
1773 | 1791 |
1774 return retval; | 1792 return retval; |
1775 } | 1793 } |
1776 | 1794 |
1777 // a unary functor that checks whether a value is outside [a,b) range | 1795 // A recursively splitting lookup of a sorted array in another sorted array. |
1778 template<class T, class Comp> | 1796 template <class T> template <class Comp> |
1779 class out_of_range_pred : public std::unary_function<T, bool> | 1797 void |
1780 { | 1798 octave_sort<T>::lookup_merge (const T *data, octave_idx_type lo, octave_idx_type hi, |
1781 public: | 1799 const T *values, octave_idx_type nvalues, |
1782 out_of_range_pred (const T& aa, const T& bb, Comp c) | 1800 octave_idx_type *idx, Comp comp) |
1783 : a (aa), b (bb), comp (c) { } | 1801 { |
1784 bool operator () (const T& x) { return comp (x, a) || ! comp (x, b); } | 1802 // Fake entry. |
1785 | 1803 begin: |
1786 private: | 1804 |
1787 T a, b; | 1805 if (hi - lo <= nvalues + 16) |
1788 Comp comp; | 1806 { |
1789 }; | 1807 // Do a linear merge. |
1790 | 1808 octave_idx_type i = lo, j = 0; |
1791 // a unary functor that checks whether a value is < a | 1809 while (j != nvalues && i < hi) |
1792 template<class T, class Comp> | 1810 { |
1793 class less_than_pred : public std::unary_function<T, bool> | 1811 if (comp (values[j], data[i])) |
1794 { | 1812 idx[j++] = i; |
1795 typedef typename ref_param<T>::type param_type; | 1813 else |
1796 public: | 1814 i++; |
1797 less_than_pred (param_type aa, Comp c) | 1815 } |
1798 : a (aa), comp (c) { } | 1816 while (j != nvalues) |
1799 bool operator () (const T& x) { return comp (x, a); } | 1817 idx[j++] = i; |
1800 | 1818 } |
1801 private: | 1819 else |
1802 T a; | 1820 { |
1803 Comp comp; | 1821 // Use the ordering to split the table; look-up recursively. |
1804 }; | 1822 switch (nvalues) |
1805 | 1823 { |
1806 // a unary functor that checks whether a value is >= a | 1824 case 0: |
1807 template<class T, class Comp> | 1825 break; |
1808 class greater_or_equal_pred : public std::unary_function<T, bool> | 1826 case 1: |
1809 { | 1827 idx[0] = lookup_impl (data, lo, hi, values[0], comp); |
1810 public: | 1828 break; |
1811 greater_or_equal_pred (const T& aa, Comp c) | 1829 case 2: |
1812 : a (aa), comp (c) { } | 1830 idx[0] = lookup_impl (data, lo, hi, values[0], comp); |
1813 bool operator () (const T& x) { return ! comp (x, a); } | 1831 idx[1] = lookup_impl (data, idx[0], hi, values[1], comp); |
1814 | 1832 break; |
1815 private: | 1833 case 3: |
1816 T a; | 1834 idx[1] = lookup_impl (data, lo, hi, values[1], comp); |
1817 Comp comp; | 1835 idx[0] = lookup_impl (data, lo, idx[1], values[0], comp); |
1818 }; | 1836 idx[2] = lookup_impl (data, idx[1], hi, values[2], comp); |
1819 | 1837 break; |
1820 // conveniently constructs the above functors. | 1838 case 4: |
1821 // NOTE: with SGI extensions, this one can be written as | 1839 idx[2] = lookup_impl (data, lo, hi, values[2], comp); |
1822 // compose2 (logical_and<bool>(), bind2nd (less<T>(), a), | 1840 idx[0] = lookup_impl (data, lo, idx[2], values[0], comp); |
1823 // not1 (bind2nd (less<T>(), b))) | 1841 idx[1] = lookup_impl (data, idx[0], idx[2], values[1], comp); |
1824 template<class T, class Comp> | 1842 idx[3] = lookup_impl (data, idx[2], hi, values[3], comp); |
1825 inline out_of_range_pred<T, Comp> | 1843 break; |
1826 out_of_range (const T& a, | 1844 case 5: |
1827 const T& b, Comp comp) | 1845 idx[2] = lookup_impl (data, lo, hi, values[2], comp); |
1828 { | 1846 idx[0] = lookup_impl (data, lo, idx[2], values[0], comp); |
1829 return out_of_range_pred<T, Comp> (a, b, comp); | 1847 idx[1] = lookup_impl (data, idx[0], idx[2], values[1], comp); |
1830 } | 1848 idx[3] = lookup_impl (data, idx[2], hi, values[3], comp); |
1831 | 1849 idx[4] = lookup_impl (data, idx[3], hi, values[4], comp); |
1832 // Note: these could be written as | 1850 break; |
1833 // std::not1 (std::bind2nd (comp, *cur)) | 1851 default: |
1834 // and | 1852 { |
1835 // std::bind2nd (comp, *(cur-1))); | 1853 // This is a bit tricky. We do not want a normal recursion |
1836 // but that doesn't work for functions with reference parameters in g++ 4.3. | 1854 // because we split by idx[k], and there's no guaranteed descend |
1837 template<class T, class Comp> | 1855 // ratio; hence the worst-case scenario would be a linear stack |
1838 inline less_than_pred<T, Comp> | 1856 // growth, which is dangerous. Instead, we recursively run the |
1839 less_than (const T& a, Comp comp) | 1857 // smaller half, and simulate a tail call for the rest using |
1840 { | 1858 // goto. |
1841 return less_than_pred<T, Comp> (a, comp); | 1859 octave_idx_type k = nvalues / 2; |
1842 } | 1860 idx[k] = lookup_impl (data, lo, hi, values[k], comp); |
1843 template<class T, class Comp> | 1861 if (idx[k] - lo <= hi - idx[k]) |
1844 inline greater_or_equal_pred<T, Comp> | 1862 { |
1845 greater_or_equal (const T& a, Comp comp) | 1863 // The smaller portion is run recursively. |
1846 { | 1864 lookup_merge (data, idx[k], k, values, k, idx, comp); |
1847 return greater_or_equal_pred<T, Comp> (a, comp); | 1865 // Simulate a tail call. |
1848 } | 1866 lo = idx[k]; |
1849 | 1867 values += k; nvalues -= k; idx += k; |
1868 goto begin; | |
1869 } | |
1870 else | |
1871 { | |
1872 // The smaller portion is run recursively. | |
1873 lookup_merge (data, idx[k], hi, | |
1874 values + k, nvalues - k, idx, comp); | |
1875 // Simulate a tail call. | |
1876 hi = idx[k]; | |
1877 nvalues = k; | |
1878 goto begin; | |
1879 } | |
1880 break; | |
1881 } | |
1882 } | |
1883 } | |
1884 } | |
1850 | 1885 |
1851 template <class T> template <class Comp> | 1886 template <class T> template <class Comp> |
1852 void | 1887 void |
1853 octave_sort<T>::lookup (const T *data, octave_idx_type nel, | 1888 octave_sort<T>::lookup (const T *data, octave_idx_type nel, |
1854 const T *values, octave_idx_type nvalues, | 1889 const T *values, octave_idx_type nvalues, |
1857 if (nel == 0) | 1892 if (nel == 0) |
1858 // the trivial case of empty table | 1893 // the trivial case of empty table |
1859 std::fill_n (idx, nvalues, offset); | 1894 std::fill_n (idx, nvalues, offset); |
1860 else | 1895 else |
1861 { | 1896 { |
1862 const T *vcur = values; | 1897 octave_idx_type vlo = 0; |
1863 const T *vend = values + nvalues; | 1898 int nbadruns = 0; |
1864 | 1899 while (vlo != nvalues && nbadruns < 16) |
1865 const T *cur = data; | |
1866 const T *end = data + nel; | |
1867 | |
1868 while (vcur != vend) | |
1869 { | 1900 { |
1870 // determine the enclosing interval for next value, trying | 1901 octave_idx_type vhi; |
1871 // ++cur as a special case; | 1902 |
1872 if (cur == end || comp (*vcur, *cur)) | 1903 // Determine a sorted run. |
1873 cur = std::upper_bound (data, cur, *vcur, comp); | 1904 for (vhi = vlo + 1; vhi != nvalues; vhi++) |
1874 else | |
1875 { | 1905 { |
1876 ++cur; | 1906 if (! comp (values[vhi-1], values[vhi])) |
1877 if (cur != end && ! comp (*vcur, *cur)) | 1907 break; |
1878 cur = std::upper_bound (cur + 1, end, *vcur, comp); | |
1879 } | 1908 } |
1880 | 1909 |
1881 octave_idx_type vidx = cur - data + offset; | 1910 // Do a recursive lookup. |
1882 // store index of the current interval. | 1911 lookup_merge (data - offset, offset, nel + offset, |
1883 *(idx++) = vidx; | 1912 values + vlo, vhi - vlo, idx + vlo, comp); |
1884 ++vcur; | 1913 |
1885 | 1914 if (vhi - vlo <= 2) |
1886 // find first value not in current subrange | 1915 nbadruns++; |
1887 const T *vnew; | 1916 else if (vhi - vlo >= 16) |
1888 if (cur != end) | 1917 nbadruns = 0; |
1889 if (cur != data) | 1918 |
1890 // inner interval | 1919 vlo = vhi; |
1891 vnew = std::find_if (vcur, vend, | |
1892 out_of_range (*(cur-1), *cur, comp)); | |
1893 else | |
1894 // special case: lowermost range (-Inf, min) | |
1895 vnew = std::find_if (vcur, vend, greater_or_equal (*cur, comp)); | |
1896 else | |
1897 // special case: uppermost range [max, Inf) | |
1898 vnew = std::find_if (vcur, vend, less_than (*(cur-1), comp)); | |
1899 | |
1900 // store index of the current interval. | |
1901 std::fill_n (idx, vnew - vcur, vidx); | |
1902 idx += (vnew - vcur); | |
1903 vcur = vnew; | |
1904 } | 1920 } |
1921 | |
1922 // The optimistic loop terminated, do the rest normally. | |
1923 for (; vlo != nvalues; vlo++) | |
1924 idx[vlo] = lookup_impl (data, 0, nel, values[vlo], comp) + offset; | |
1905 } | 1925 } |
1906 } | 1926 } |
1907 | 1927 |
1908 template <class T> | 1928 template <class T> |
1909 void | 1929 void |
1929 void | 1949 void |
1930 octave_sort<T>::lookupm (const T *data, octave_idx_type nel, | 1950 octave_sort<T>::lookupm (const T *data, octave_idx_type nel, |
1931 const T *values, octave_idx_type nvalues, | 1951 const T *values, octave_idx_type nvalues, |
1932 octave_idx_type *idx, Comp comp) | 1952 octave_idx_type *idx, Comp comp) |
1933 { | 1953 { |
1934 const T *end = data + nel; | |
1935 for (octave_idx_type i = 0; i < nvalues; i++) | 1954 for (octave_idx_type i = 0; i < nvalues; i++) |
1936 { | 1955 { |
1937 const T *ptr = std::lower_bound (data, end, values[i], comp); | 1956 octave_idx_type j = lookup_impl (data, 0, nel, values[i], comp); |
1938 if (ptr != end && ! comp (values[i], *ptr)) | 1957 idx[i] = (j != 0 && comp (data[j-1], values[i])) ? -1 : j-1; |
1939 idx[i] = ptr - data; | |
1940 else | |
1941 idx[i] = -1; | |
1942 } | 1958 } |
1943 } | 1959 } |
1944 | 1960 |
1945 template <class T> | 1961 template <class T> |
1946 void | 1962 void |
1966 void | 1982 void |
1967 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, | 1983 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, |
1968 const T *values, octave_idx_type nvalues, | 1984 const T *values, octave_idx_type nvalues, |
1969 bool *match, Comp comp) | 1985 bool *match, Comp comp) |
1970 { | 1986 { |
1971 const T *end = data + nel; | |
1972 for (octave_idx_type i = 0; i < nvalues; i++) | 1987 for (octave_idx_type i = 0; i < nvalues; i++) |
1973 match[i] = std::binary_search (data, end, values[i], comp); | 1988 { |
1989 octave_idx_type j = lookup_impl (data, 0, nel, values[i], comp); | |
1990 match[i] = (j != 0 && ! comp (data[j-1], values[i])); | |
1991 } | |
1974 } | 1992 } |
1975 | 1993 |
1976 template <class T> | 1994 template <class T> |
1977 void | 1995 void |
1978 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, | 1996 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, |