1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383
|
/*
*class++
* Name:
* PolyMap
* Purpose:
* Map coordinates using polynomial functions.
* Constructor Function:
c astPolyMap
f AST_POLYMAP
* Description:
* A PolyMap is a form of Mapping which performs a general polynomial
* transformation. Each output coordinate is a polynomial function of
* all the input coordinates. The coefficients are specified separately
* for each output coordinate. The forward and inverse transformations
* are defined independantly by separate sets of coefficients. If no
* inverse transformation is supplied, an iterative method can be used
* to evaluate the inverse based only on the forward transformation.
* Inheritance:
* The PolyMap class inherits from the Mapping class.
* Attributes:
* In addition to those attributes common to all Mappings, every
* PolyMap also has the following attributes:
*
* - IterInverse: Provide an iterative inverse transformation?
* - NiterInverse: Maximum number of iterations for iterative inverse
* - TolInverse: Target relative error for iterative inverse
* Functions:
c In addition to those functions applicable to all Objects, the
c following functions may also be applied to all Mappings:
f In addition to those routines applicable to all Objects, the
f following routines may also be applied to all Mappings:
*
c - astPolyTran: Fit a PolyMap inverse or forward transformation
f - AST_POLYTRAN: Fit a PolyMap inverse or forward transformation
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* Copyright (C) 2011 Science & Technology Facilities Council.
* All Rights Reserved.
* Licence:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence as
* published by the Free Software Foundation; either version 2 of
* the Licence, or (at your option) any later version.
*
* This program is distributed in the hope that it will be
* useful,but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public Licence
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
* 02110-1301, USA
* Authors:
* DSB: D.S. Berry (Starlink)
* History:
* 27-SEP-2003 (DSB):
* Original version.
* 13-APR-2005 (DSB):
* Changed the keys used by the Dump/astLoadPolyMap functions. They
* used to exceed 8 characters and consequently caused problems for
* FitsChans.
* 20-MAY-2005 (DSB):
* Correct the indexing of keywords produced in the Dump function.
* 20-APR-2006 (DSB):
* Guard against undefined transformations in Copy.
* 10-MAY-2006 (DSB):
* Override astEqual.
* 4-JUL-2008 (DSB):
* Fixed loop indexing problems in Equal function.
* 27-MAY-2011 (DSB):
* Added public method astPolyTran.
* 18-JUL-2011 (DSB):
* - Added attributes IterInverse, NiterInverse and TolInverse.
* - Do not report an error if astPolyTran fails to fit an inverse.
* 15-OCT-2011 (DSB):
* Improve argument checking and error reporting in PolyTran
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS PolyMap
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "globals.h" /* Thread-safe global data access */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory allocation facilities */
#include "object.h" /* Base Object class */
#include "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate mappings (parent class) */
#include "cmpmap.h" /* Compound mappings */
#include "polymap.h" /* Interface definition for this class */
#include "unitmap.h" /* Unit mappings */
#include "levmar.h" /* Levenberg - Marquardt minimization */
#include "pal.h" /* SLALIB function definitions */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* Module Variables. */
/* ================= */
/* Address of this static variable is used as a unique identifier for
member of this class. */
static int class_check;
/* Pointers to parent class methods which are extended by this class. */
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *(* parent_getattrib)( AstObject *, const char *, int * );
static int (* parent_testattrib)( AstObject *, const char *, int * );
static void (* parent_clearattrib)( AstObject *, const char *, int * );
static void (* parent_setattrib)( AstObject *, const char *, int * );
static int (* parent_getobjsize)( AstObject *, int * );
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0; \
globals->GetAttrib_Buff[ 0 ] = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(PolyMap)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(PolyMap,Class_Init)
#define class_vtab astGLOBAL(PolyMap,Class_Vtab)
#define getattrib_buff astGLOBAL(LutMap,GetAttrib_Buff)
#include <pthread.h>
#else
static char getattrib_buff[ 101 ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstPolyMapVtab class_vtab; /* Virtual function table */
static int class_init = 0; /* Virtual function table initialised? */
#endif
/* Type Definitions */
/* ================ */
/* Structure used to pass data to the Levenberg - Marquardt non-linear
minimization algorithm. */
typedef struct LevMarData {
int order; /* Max power of X1 or X2, plus one. */
int nsamp; /* No. of polynomial samples to fit */
int init_jac; /* Has the constant Jacobian been found yet? */
double *xp1; /* Pointer to powers of X1 (1st poly i/p) at all samples */
double *xp2; /* Pointer to powers of X2 (2nd poly i/p) at all samples */
double *y[ 2 ]; /* Pointers to Y1 and Y2 values at all samples */
} LevMarData;
/* External Interface Function Prototypes. */
/* ======================================= */
/* The following functions have public prototypes only (i.e. no
protected prototypes), so we must provide local prototypes for use
within this module. */
AstPolyMap *astPolyMapId_( int, int, int, const double[], int, const double[], const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static AstPolyMap **GetJacobian( AstPolyMap *, int * );
static AstPolyMap *PolyTran( AstPolyMap *, int, double, double, int, const double *, const double *, int * );
static double **SamplePoly1D( AstPolyMap *, int, double **, double, double, int, int *, double[2], int * );
static double **SamplePoly2D( AstPolyMap *, int, double **, const double *, const double *, int, int *, double[4], int * );
static double *FitPoly1D( int, double, int, double **, double[2], int *, double *, int * );
static double *FitPoly2D( int, double, int, double **, double[4], int *, double *, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetObjSize( AstObject *, int * );
static int GetTranForward( AstMapping *, int * );
static int GetTranInverse( AstMapping *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int ReplaceTransformation( AstPolyMap *, int, double, double, int, const double *, const double *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *obj, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void FreeArrays( AstPolyMap *, int, int * );
static void IterInverse( AstPolyMap *, AstPointSet *, AstPointSet *, int * );
static void LMFunc1D( double *, double *, int, int, void * );
static void LMFunc2D( double *, double *, int, int, void * );
static void LMJacob1D( double *, double *, int, int, void * );
static void LMJacob2D( double *, double *, int, int, void * );
static void StoreArrays( AstPolyMap *, int, int, const double *, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
static const char *GetAttrib( AstObject *, const char *, int * );
static int TestAttrib( AstObject *, const char *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void SetAttrib( AstObject *, const char *, int * );
static int GetIterInverse( AstPolyMap *, int * );
static int TestIterInverse( AstPolyMap *, int * );
static void ClearIterInverse( AstPolyMap *, int * );
static void SetIterInverse( AstPolyMap *, int, int * );
static int GetNiterInverse( AstPolyMap *, int * );
static int TestNiterInverse( AstPolyMap *, int * );
static void ClearNiterInverse( AstPolyMap *, int * );
static void SetNiterInverse( AstPolyMap *, int, int * );
static double GetTolInverse( AstPolyMap *, int * );
static int TestTolInverse( AstPolyMap *, int * );
static void ClearTolInverse( AstPolyMap *, int * );
static void SetTolInverse( AstPolyMap *, double, int * );
/* Member functions. */
/* ================= */
static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* ClearAttrib
* Purpose:
* Clear an attribute value for a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* void ClearAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* PolyMap member function (over-rides the astClearAttrib protected
* method inherited from the Mapping class).
* Description:
* This function clears the value of a specified attribute for a
* PolyMap, so that the default value will subsequently be used.
* Parameters:
* this
* Pointer to the PolyMap.
* attrib
* Pointer to a null-terminated string specifying the attribute
* name. This should be in lower case with no surrounding white
* space.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPolyMap *this; /* Pointer to the PolyMap structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Check the attribute name and clear the appropriate attribute. */
/* IterInverse. */
/* ------------ */
if ( !strcmp( attrib, "iterinverse" ) ) {
astClearIterInverse( this );
/* NiterInverse. */
/* ------------- */
} else if ( !strcmp( attrib, "niterinverse" ) ) {
astClearNiterInverse( this );
/* TolInverse. */
/* ----------- */
} else if ( !strcmp( attrib, "tolinverse" ) ) {
astClearTolInverse( this );
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
(*parent_clearattrib)( this_object, attrib, status );
}
}
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two PolyMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* PolyMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two PolyMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a PolyMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the PolyMaps are equivalent, zero otherwise.
* Notes:
* - A value of zero will be returned if this function is invoked
* with the global status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstPolyMap *that;
AstPolyMap *this;
int i, j, k;
int nin;
int nout;
int result;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two PolyMap structures. */
this = (AstPolyMap *) this_object;
that = (AstPolyMap *) that_object;
/* Check the second object is a PolyMap. We know the first is a
PolyMap since we have arrived at this implementation of the virtual
function. */
if( astIsAPolyMap( that ) ) {
/* Get the number of inputs and outputs and check they are the same for both. */
nin = astGetNin( this );
nout = astGetNout( this );
if( astGetNin( that ) == nin && astGetNout( that ) == nout ) {
/* If the Invert flags for the two PolyMaps differ, it may still be possible
for them to be equivalent. First compare the PolyMaps if their Invert
flags are the same. In this case all the attributes of the two PolyMaps
must be identical. */
if( astGetInvert( this ) == astGetInvert( that ) ) {
result = 1;
for( i = 0; i < nout && result; i++ ) {
if( this->ncoeff_f[ i ] != that->ncoeff_f[ i ] ||
this->mxpow_i[ i ] != that->mxpow_i[ i ] ) {
result = 0;
}
}
if( this->coeff_f && that->coeff_f ) {
for( i = 0; i < nout && result; i++ ) {
for( j = 0; j < this->ncoeff_f[ i ] && result; j++ ) {
if( !EQUAL( this->coeff_f[ i ][ j ],
that->coeff_f[ i ][ j ] ) ) {
result = 0;
}
}
}
}
if( this->power_f && that->power_f ) {
for( i = 0; i < nout && result; i++ ) {
for( j = 0; j < this->ncoeff_f[ i ] && result; j++ ) {
for( k = 0; k < nin && result; k++ ) {
if( !EQUAL( this->power_f[ i ][ j ][ k ],
that->power_f[ i ][ j ][ k ] ) ) {
result = 0;
}
}
}
}
}
for( i = 0; i < nin && result; i++ ) {
if( this->ncoeff_i[ i ] != that->ncoeff_i[ i ] ||
this->mxpow_f[ i ] != that->mxpow_f[ i ] ) {
result = 0;
}
}
if( this->coeff_i && that->coeff_i ) {
for( i = 0; i < nin && result; i++ ) {
for( j = 0; j < this->ncoeff_i[ i ] && result; j++ ) {
if( !EQUAL( this->coeff_i[ i ][ j ],
that->coeff_i[ i ][ j ] ) ) {
result = 0;
}
}
}
}
if( this->power_i && that->power_i ) {
for( i = 0; i < nin && result; i++ ) {
for( j = 0; j < this->ncoeff_i[ i ] && result; j++ ) {
for( k = 0; k < nout && result; k++ ) {
if( !EQUAL( this->power_i[ i ][ j ][ k ],
that->power_i[ i ][ j ][ k ] ) ) {
result = 0;
}
}
}
}
}
/* If the Invert flags for the two PolyMaps differ, the attributes of the two
PolyMaps must be inversely related to each other. */
} else {
/* In the specific case of a PolyMap, Invert flags must be equal. */
result = 0;
}
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static double *FitPoly1D( int nsamp, double acc, int order, double **table,
double scales[2], int *ncoeff, double *racc,
int *status ){
/*
* Name:
* FitPoly1D
* Purpose:
* Fit a (1-in,1-out) polynomial to a supplied set of data.
* Type:
* Private function.
* Synopsis:
* double *FitPoly1D( int nsamp, double acc, int order, double **table,
* double scales[2], int *ncoeff, double *racc,
* int *status )
* Description:
* This function fits a least squares 1D polynomial curve to the
* positions in a supplied table. For the purposes of this function,
* the polynomial input is refered to as x1 and the output as y1. So
* the polynomial is:
*
* y1 = P1( x1 )
* Parameters:
* nsamp
* The number of (x1,y1) positions in the supplied table.
* acc
* The required accuracy, expressed as an offset within the polynomials
* output space.
* order
* The maximum power (minus one) of x1 within P1. So for instance, a
* value of 3 refers to a quadratic polynomial.
* table
* Pointer to an array of 2 pointers. Each of these pointers points
* to an array of "nsamp" doubles, being the scaled and sampled values
* for x1 and y1 in that order.
* scales
* Array holding the scaling factors for the two columns of the table.
* Multiplying the table values by the scale factor produces PolyMap
* input or output axis values.
* ncoeff
* Pointer to an ant in which to return the number of coefficients
* described by the returned array.
* racc
* Pointer to a double in which to return the achieved accuracy
* (which may be greater than "acc").
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to an array of doubles defining the polynomial in the
* form required by the PolyMap contructor. The number of coefficients
* is returned via "ncoeff". If the polynomial could not be found with
* sufficient accuracy , then NULL is returned. The returned pointer
* should be freed using astFree when no longer needed.
*/
/* Local Variables: */
LevMarData data;
double *coeffs;
double *pc;
double *pr;
double *px1;
double *pxp1;
double *result;
double f1;
double f2;
double facc;
double info[10];
double maxterm;
double term;
double tv;
double x1;
int k;
int ncof;
int niter;
int w1;
/* Termination criteria for the minimisation - see levmar.c */
double opts[] = { 1E-3, 1E-7, 1E-10, 1E-17 };
/* Initialise returned value */
result = NULL;
*ncoeff = 0;
*racc = 10*acc;
/* Check inherited status */
if( !astOK ) return result;
/* Number of coefficients per poly. */
ncof = order;
/* Initialise the elements of the structure. */
data.order = order;
data.nsamp = nsamp;
data.init_jac = 1;
data.xp1 = astMalloc( nsamp*order*sizeof( double ) );
data.xp2 = NULL;
data.y[ 0 ] = table[ 1 ];
data.y[ 1 ] = NULL;
/* Work space to hold coefficients. */
coeffs = astMalloc( ncof*sizeof( double ) );
if( astOK ) {
/* Store required squared acccuracy, taking account of the fact that the
optimisation uses scaled axis values rather than PolyMap axis values. */
facc = 1.0/(scales[1]*scales[1]);
opts[ 3 ] = nsamp*acc*acc*facc;
/* Get pointers to the supplied x1 values. */
px1 = table[ 0 ];
/* Get pointers to the location for the next power of x1. */
pxp1 = data.xp1;
/* Loop round all samples. */
for( k = 0; k < nsamp; k++ ) {
/* Get the current x1 value. */
x1 = *(px1++);
/* Find all the required powers of x1 and store them in the "xp1"
component of the data structure. */
tv = 1.0;
for( w1 = 0; w1 < order; w1++ ) {
*(pxp1++) = tv;
tv *= x1;
}
}
/* The initial guess at the coefficient values represents a unit
transformation in PolyMap axis values. */
for( k = 0; k < ncof; k++ ) coeffs[ k ] = 0.0;
coeffs[ 1 ] = scales[ 0 ]/scales[ 1 ];
/* Find the best coefficients */
niter = dlevmar_der( LMFunc1D, LMJacob1D, coeffs, NULL, ncof, nsamp,
1000, opts, info, NULL, NULL, &data );
/* Return the achieved accuracy. */
*racc = sqrt( info[ 1 ]/(nsamp*facc) );
/* The best fitting polynomial coefficient found above relate to the
polynomial between the scaled positions stored in "table". These
scaled positions are related to PolyMap input/output axis values via
the scale factors supplied in "scales". Find the initial factor for the
current output. */
f1 = scales[ 1 ];
f2 = 1.0;
/* Look at each coefficient. */
pc = coeffs;
for( w1 = 0; w1 < order; w1++,pc++ ) {
/* Get a pointer to the powers of X1 appropriate for the current coefficient,
at the first sample. */
pxp1 = data.xp1 + w1;
/* We find the contribution which this coefficient makes to the total
polynomial value. Find the maximum contribution made at any sample
points. */
maxterm = 0.0;
for( k = 0; k < nsamp; k++ ) {
/* Get the absolute value of the polynomial term that uses the current
coefficient. */
term = fabs( ( *pc )*( *pxp1 ) );
/* Update the maximum term found at any sample. */
if( term > maxterm ) maxterm = term;
/* Increment the pointers to refer to the next sample. */
pxp1 += order;
}
/* If the maximum contribution made by this term is less than the
required accuracy, set the coefficient value to zero. */
if( maxterm*f1 < acc ) {
*pc = 0.0;
/* Scale the best fitting polynomial coefficient found above to take
account of the fact that the tabulated input and output positions in
"table" were are not actual PolyMap input and output axis values, but
are scaled by the factors stored in "scales". */
} else {
*pc *= f1/f2;
}
f2 *= scales[ 0 ];
}
/* Convert the array of coefficients into PolyMap form. */
result = astMalloc( ncof*3*sizeof( double ) );
*ncoeff = 0;
pr = result;
pc = coeffs;
for( w1 = 0; w1 < order; w1++,pc++ ) {
if( *pc != 0.0 ) {
*(pr++) = *pc;
*(pr++) = 1;
*(pr++) = w1;
(*ncoeff)++;
}
}
/* Truncate the returned array. */
result = astRealloc( result, (*ncoeff)*3*sizeof( double ) );
}
/* Free resources. */
coeffs = astFree( coeffs );
data.xp1 = astFree( data.xp1 );
/* Return the coefficient array. */
return result;
}
static double *FitPoly2D( int nsamp, double acc, int order, double **table,
double scales[4], int *ncoeff, double *racc,
int *status ){
/*
* Name:
* FitPoly2D
* Purpose:
* Fit a (2-in,2-out) polynomial to a supplied set of data.
* Type:
* Private function.
* Synopsis:
* double *FitPoly2D( int nsamp, double acc, int order, double **table,
* double scales[4], int *ncoeff, double *racc,
* int *status )
* Description:
* This function fits a pair of least squares 2D polynomial surfaces
* to the positions in a supplied table. For the purposes of this
* function, the polynomial inputs are refered to as (x1,x2) and the
* outputs as (y1,y2). So the two polynomials are:
*
* y1 = P1( x1, x2 )
* y2 = P2( x1, x2 )
*
* P1 and P2 have the same maximum power on each input (specified by
* the "order" parameter).
* Parameters:
* nsamp
* The number of (x1,x2,y1,y2) positions in the supplied table.
* acc
* The required accuracy, expressed as a geodesic distance within
* the polynomials output space.
* order
* The maximum power (minus one) of x1 or x2 within P1 and P2. So for
* instance, a value of 3 refers to a quadratic polynomial. Note, cross
* terms with total powers greater than or equal to "order" are not
* inlcuded in the fit. So the maximum number of terms in the fitted
* polynomial is order*(order+1)/2.
* table
* Pointer to an array of 4 pointers. Each of these pointers points
* to an array of "nsamp" doubles, being the scaled and sampled values
* for x1, x2, y1 or y2 in that order.
* scales
* Array holding the scaling factors for the four columns of the table.
* Multiplying the table values by the scale factor produces PolyMap
* input or output axis values.
* ncoeff
* Pointer to an ant in which to return the number of coefficients
* described by the returned array.
* racc
* Pointer to a double in which to return the achieved accuracy
* (which may be greater than "acc").
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to an array of doubles defining the polynomial in the
* form required by the PolyMap contructor. The number of coefficients
* is returned via "ncoeff". If the polynomial could not be found with
* sufficient accuracy , then NULL is returned. The returned pointer
* should be freed using astFree when no longer needed.
*/
/* Local Variables: */
LevMarData data;
double *coeffs;
double *pc;
double *pr;
double *px1;
double *px2;
double *pxp1;
double *pxp2;
double *result;
double f1;
double f2;
double f20;
double f3;
double facc;
double info[10];
double maxterm;
double term;
double tv;
double x1;
double x2;
int iout;
int k;
int ncof;
int niter;
int w12;
int w1;
int w2;
/* Termination criteria for the minimisation - see levmar.c */
double opts[] = { 1E-3, 1E-7, 1E-10, 1E-17 };
/* Initialise returned value */
result = NULL;
*ncoeff = 0;
*racc = 10*acc;
/* Check inherited status */
if( !astOK ) return result;
/* Number of coefficients per poly. */
ncof = order*( order + 1 )/2;
/* Initialise the elements of the structure. */
data.order = order;
data.nsamp = nsamp;
data.init_jac = 1;
data.xp1 = astMalloc( nsamp*order*sizeof( double ) );
data.xp2 = astMalloc( nsamp*order*sizeof( double ) );
data.y[ 0 ] = table[ 2 ];
data.y[ 1 ] = table[ 3 ];
/* Work space to hold coefficients. */
coeffs = astMalloc( 2*ncof*sizeof( double ) );
if( astOK ) {
/* Store required squared acccuracy, taking account of the fact that the
optimisation uses scaled axis values rather than PolyMap axis values. */
facc = 1.0/(scales[2]*scales[2]) + 1.0/(scales[3]*scales[3]);
opts[ 3 ] = nsamp*acc*acc*facc;
/* Get pointers to the supplied x1 and x2 values. */
px1 = table[ 0 ];
px2 = table[ 1 ];
/* Get pointers to the location for the next power of x1 and x2. */
pxp1 = data.xp1;
pxp2 = data.xp2;
/* Loop round all samples. */
for( k = 0; k < nsamp; k++ ) {
/* Get the current x1 and x2 values. */
x1 = *(px1++);
x2 = *(px2++);
/* Find all the required powers of x1 and store them in the "xp1"
component of the data structure. */
tv = 1.0;
for( w1 = 0; w1 < order; w1++ ) {
*(pxp1++) = tv;
tv *= x1;
}
/* Find all the required powers of x2 and store them in the "xp2"
comonent of the data structure. */
tv = 1.0;
for( w2 = 0; w2 < order; w2++ ) {
*(pxp2++) = tv;
tv *= x2;
}
}
/* The initial guess at the coefficient values represents a unit
transformation in PolyMap axis values. */
for( k = 0; k < 2*ncof; k++ ) coeffs[ k ] = 0.0;
coeffs[ 1 ] = scales[ 0 ]/scales[ 2 ];
coeffs[ 2 ] = scales[ 1 ]/scales[ 3 ];
/* Find the best coefficients */
niter = dlevmar_der( LMFunc2D, LMJacob2D, coeffs, NULL, 2*ncof, 2*nsamp,
1000, opts, info, NULL, NULL, &data );
/* Return the achieved accuracy. */
*racc = sqrt( info[ 1 ]/(nsamp*facc) );
/* Pointer to the first coefficient. */
pc = coeffs;
/* Look at coefficients for each output in turn. */
for( iout = 0; iout < 2 && astOK; iout++ ) {
/* The best fitting polynomial coefficient found above relate to the
polynomial between the scaled positions stored in "table". These
scaled positions are related to PolyMap input/output axis values via
the scale factors supplied in "scales". Find the initial factor for the
current output. */
f1 = scales[ 2 + iout ];
/* Look at each coefficient for the current output. */
f20 = 1.0;
for( w12 = 0; w12 < order; w12++ ) {
f3 = 1.0;
f2 = f20;
for( w2 = 0; w2 <= w12; w2++,pc++ ) {
w1 = w12 - w2;
/* Get pointers to the powers of X1 and X2 appropriate for the current
coefficient, at the first sample. */
pxp1 = data.xp1 + w1;
pxp2 = data.xp2 + w2;
/* We find the contribution which this coefficient makes to the total
polynomial value. Find the maximum contribution made at any sample
points. */
maxterm = 0.0;
for( k = 0; k < nsamp; k++ ) {
/* Get the absolute value of the polynomial term that uses the current
coefficient. */
term = fabs( ( *pc )*( *pxp1 )*( *pxp2 ) );
/* Update the maximum term found at any sample. */
if( term > maxterm ) maxterm = term;
/* Increment the pointers to refer to the next sample. */
pxp1 += order;
pxp2 += order;
}
/* If the maximum contribution made by this term is less than the
required accuracy, set the coefficient value to zero. */
if( maxterm*f1 < acc ) {
*pc = 0.0;
/* Scale the best fitting polynomial coefficient found above to take
account of the fact that the tabulated input and output positions in
"table" were are not actual PolyMap input and output axis values, but
are scaled by the factors stored in "scales". */
} else {
*pc *= f1/( f2*f3 );
}
f2 /= scales[ 0 ];
f3 *= scales[ 1 ];
}
f20 *= scales[ 0 ];
}
}
/* Convert the array of coefficients into PolyMap form. */
result = astMalloc( 2*ncof*4*sizeof( double ) );
*ncoeff = 0;
pr = result;
pc = coeffs;
for( iout = 0; iout < 2 && astOK; iout++ ) {
for( w12 = 0; w12 < order; w12++ ) {
for( w2 = 0; w2 <= w12; w2++,pc++ ) {
w1 = w12 - w2;
if( *pc != 0.0 ) {
*(pr++) = *pc;
*(pr++) = iout + 1;
*(pr++) = w1;
*(pr++) = w2;
(*ncoeff)++;
}
}
}
}
/* Truncate the returned array. */
result = astRealloc( result, (*ncoeff)*4*sizeof( double ) );
}
/* Free resources. */
coeffs = astFree( coeffs );
data.xp1 = astFree( data.xp1 );
data.xp2 = astFree( data.xp2 );
/* Return the coefficient array. */
return result;
}
static void FreeArrays( AstPolyMap *this, int forward, int *status ) {
/*
* Name:
* FreeArrays
* Purpose:
* Free the dynamic arrays contained within a PolyMap
* Type:
* Private function.
* Synopsis:
* void FreeArrays( AstPolyMap *this, int forward, int *status )
* Description:
* This function frees all the dynamic arrays allocated as part of a
* PolyMap.
* Parameters:
* this
* Pointer to the PolyMap.
* forward
* If non-zero, the arrays for the forward transformation are freed.
* Otherwise, the arrays for the inverse transformation are freed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
int nin; /* Number of inputs */
int nout; /* Number of outputs */
int i; /* Loop count */
int j; /* Loop count */
/* Get the number of inputs and outputs of the uninverted Mapping. */
nin = ( (AstMapping *) this )->nin;
nout = ( (AstMapping *) this )->nout;
/* Free the dynamic arrays for the forward transformation. */
if( forward ) {
if( this->coeff_f ) {
for( i = 0; i < nout; i++ ) {
this->coeff_f[ i ] = astFree( this->coeff_f[ i ] );
}
this->coeff_f = astFree( this->coeff_f );
}
if( this->power_f ) {
for( i = 0; i < nout; i++ ) {
if( this->ncoeff_f && this->power_f[ i ] ) {
for( j = 0; j < this->ncoeff_f[ i ]; j++ ) {
this->power_f[ i ][ j ] = astFree( this->power_f[ i ][ j ] );
}
}
this->power_f[ i ] = astFree( this->power_f[ i ] );
}
this->power_f = astFree( this->power_f );
}
this->ncoeff_f = astFree( this->ncoeff_f );
this->mxpow_f = astFree( this->mxpow_f );
/* Free the dynamic arrays for the inverse transformation. */
} else {
if( this->coeff_i ) {
for( i = 0; i < nin; i++ ) {
this->coeff_i[ i ] = astFree( this->coeff_i[ i ] );
}
this->coeff_i = astFree( this->coeff_i );
}
if(this->power_i ) {
for( i = 0; i < nin; i++ ) {
if( this->ncoeff_i && this->power_i[ i ] ) {
for( j = 0; j < this->ncoeff_i[ i ]; j++ ) {
this->power_i[ i ][ j ] = astFree( this->power_i[ i ][ j ] );
}
}
this->power_i[ i ] = astFree( this->power_i[ i ] );
}
this->power_i = astFree( this->power_i );
}
this->ncoeff_i = astFree( this->ncoeff_i );
this->mxpow_i = astFree( this->mxpow_i );
}
}
static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* GetAttrib
* Purpose:
* Get the value of a specified attribute for a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* PolyMap member function (over-rides the protected astGetAttrib
* method inherited from the Mapping class).
* Description:
* This function returns a pointer to the value of a specified
* attribute for a PolyMap, formatted as a character string.
* Parameters:
* this
* Pointer to the PolyMap.
* attrib
* Pointer to a null-terminated string containing the name of
* the attribute whose value is required. This name should be in
* lower case, with all white space removed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* - Pointer to a null-terminated string containing the attribute
* value.
* Notes:
* - The returned string pointer may point at memory allocated
* within the PolyMap, or at static memory. The contents of the
* string may be over-written or the pointer may become invalid
* following a further invocation of the same function or any
* modification of the PolyMap. A copy of the string should
* therefore be made if necessary.
* - A NULL pointer will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstPolyMap *this; /* Pointer to the PolyMap structure */
const char *result; /* Pointer value to return */
double dval; /* Floating point attribute value */
int ival; /* Integer attribute value */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_object);
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Compare "attrib" with each recognised attribute name in turn,
obtaining the value of the required attribute. If necessary, write
the value into "getattrib_buff" as a null-terminated string in an appropriate
format. Set "result" to point at the result string. */
/* IterInverse. */
/* ------------ */
if ( !strcmp( attrib, "iterinverse" ) ) {
ival = astGetIterInverse( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* NiterInverse. */
/* ------------- */
} else if ( !strcmp( attrib, "niterinverse" ) ) {
ival = astGetNiterInverse( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* TolInverse. */
/* ----------- */
} else if ( !strcmp( attrib, "tolinverse" ) ) {
dval = astGetTolInverse( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval );
result = getattrib_buff;
}
/* If the attribute name was not recognised, pass it on to the parent
method for further interpretation. */
} else {
result = (*parent_getattrib)( this_object, attrib, status );
}
/* Return the result. */
return result;
}
static AstPolyMap **GetJacobian( AstPolyMap *this, int *status ){
/*
* Name:
* GetJacobian
* Purpose:
* Get a description of a Jacobian matrix for the fwd transformation
* of a PolyMap.
* Type:
* Private function.
* Synopsis:
* AstPolyMap **GetJacobian( AstPolyMap *this, int *status )
* Description:
* This function returns a set of PolyMaps which define the Jacobian
* matrix of the forward transformation of the supplied PolyMap.
*
* The Jacobian matrix has "nout" rows and "nin" columns, where "nin"
* and "nout" are the number of inputs and outputs of the supplied PolyMap.
* Row "i", column "j" of the matrix holds the rate of change of the
* i'th PolyMap output with respect to the j'th PolyMap input.
*
* Since the values in the Jacobian matrix vary across the input space
* of the PolyMap, the matrix is returned in the form of a set of new
* PolyMaps which generate the elements of the Jacobian for any given
* position in the input space. The "nout" values in a single column of
* the Jacobian matrix are generated by the "nout" outputs from a single
* new PolyMap. The whole matrix is described by "nin" PolyMaps.
*
* The returned PolyMaps are cached in the supplied PolyMap object in
* order to speed up subsequent calls to this function.
* Parameters:
* this
* The PolyMap for which the Jacbian is required.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to an array of "nin" PolyMap pointers, where "nin" is the number
* of inputs for the sipplied PolyMap. The returned array should not be changed
* in any way, and the PolyMaps should not be freed (they will be freed when
* the supplied PolyMap is deleted).
*/
/* Local Variables: */
double *coeffs;
double *pc;
int icof;
int icol;
int iin;
int irow;
int ncof;
int ncof_row;
int ncof_total;
int nin;
int nout;
int power;
/* Check inherited status */
if( !astOK ) return NULL;
/* Ensure there is a Jacobian stored in the PolyMap. */
if( !this->jacobian ) {
/* Get the number of inputs and outputs. */
nin = astGetNin( this );
nout = astGetNout( this );
/* Allocate memory to hold pointers to the PolyMaps used to describe the
Jacobian matrix. */
this->jacobian = astCalloc( nin, sizeof(AstPolyMap *) );
/* Find the total number of coefficients used to describe the supplied
PolyMap (forward transformation) and allocate work space to hold the
coefficients for a single new PolyMap forward transformation. */
ncof = 0;
for( irow = 0; irow < nout; irow++ ) {
ncof += this->ncoeff_f[ irow ];
}
coeffs = astMalloc( ncof*( 2 + nin )*sizeof( double ) );
/* Check pointers can be used safely. */
if( astOK ) {
/* The Jacobian matrix has "nout" rows and "nin" columns. The "nout" values
in a single column of the Jacobian matrix corresponds to the "nout" outputs
from a single PolyMap. The whole matrix is described by "nin" PolyMaps.
Loop over each column of the Matrix, creating the corresponding PolyMap
for each. */
for( icol = 0; icol < nin; icol++ ) {
/* Initialise the total number of coefficients used to describe the
element of the PolyMap. */
ncof_total = 0;
/* Loop over each row of the Jacobian matrix (i.e. each PolyMap output). */
pc = coeffs;
for( irow = 0; irow < nout; irow++ ) {
/* Loop over each coefficient used in the polynomial that generates the
current PolyMap output. */
ncof_row = this->ncoeff_f[ irow ];
for( icof = 0; icof < ncof_row; icof++ ) {
/* Get the power of input "icol" associated with the current coefficient. */
power = (int)( this->power_f[ irow ][ icof ][ icol ] + 0.5 );
/* We can skip the coefficient if the power is zero. */
if( power > 0 ) {
ncof_total++;
/* Store the coefficient value, modified so that it describes a
polynomial that has been differentiated with respect to input "icol". */
*(pc++) = this->coeff_f[ irow ][ icof ]*power;
/* Store the output PolyMap to which the coeff relates. */
*(pc++) = irow + 1;
/* Store the powers of the inputs associated with the coeff. These are
the same as the original powers, except that the power of "icol"
(the input with respect to which the output has been differentiated)
is reduced by one. */
for( iin = 0; iin < nin; iin++ ) {
if( iin != icol ) {
*(pc++) = this->power_f[ irow ][ icof ][ iin ];
} else {
*(pc++) = this->power_f[ irow ][ icof ][ iin ] - 1;
}
}
}
}
}
/* Create the PolyMap and store a pointer to it in the jacobian array in
the supplied PolyMap. */
(this->jacobian)[ icol ] = astPolyMap( nin, nout, ncof_total, coeffs,
0, NULL, " ", status );
}
}
/* Free resources */
coeffs = astFree( coeffs );
}
/* Return the Jacobian. */
return this->jacobian;
}
static int GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* PolyMap member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied PolyMap,
* in bytes.
* Parameters:
* this
* Pointer to the PolyMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* Notes:
* - A value of zero will be returned if this function is invoked
* with the global status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstPolyMap *this;
int ic;
int nc;
int result;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by this class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
if( this->jacobian ) {
nc = astGetNin( this );
for( ic = 0; ic < nc; ic++ ) {
result += astGetObjSize( (this->jacobian)[ ic ] );
}
result += sizeof( AstPolyMap * )*nc;
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static int GetTranForward( AstMapping *this, int *status ) {
/*
*
* Name:
* GetTranForward
* Purpose:
* Determine if a PolyMap defines a forward coordinate transformation.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int GetTranForward( AstMapping *this, int *status )
* Class Membership:
* PolyMap member function (over-rides the astGetTranForward method
* inherited from the Mapping class).
* Description:
* This function returns a value indicating if the PolyMap is able
* to perform a forward coordinate transformation.
* Parameters:
* this
* Pointer to the PolyMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the forward coordinate transformation is not defined, or 1 if it
* is.
* Notes:
* - A value of zero will be returned if this function is invoked with the
* global error status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstPolyMap *map; /* Pointer to PolyMap to be queried */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Obtain a pointer to the PolyMap. */
map = (AstPolyMap *) this;
/* Return the result. */
return map->ncoeff_f ? 1 : 0;
}
static int GetTranInverse( AstMapping *this, int *status ) {
/*
*
* Name:
* GetTranInverse
* Purpose:
* Determine if a PolyMap defines an inverse coordinate transformation.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int GetTranInverse( AstMapping *this, int *status )
* Class Membership:
* PolyMap member function (over-rides the astGetTranInverse method
* inherited from the Mapping class).
* Description:
* This function returns a value indicating if the PolyMap is able
* to perform an inverse coordinate transformation.
* Parameters:
* this
* Pointer to the PolyMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the inverse coordinate transformation is not defined, or 1 if it
* is.
* Notes:
* - A value of zero will be returned if this function is invoked with the
* global error status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstPolyMap *map; /* Pointer to PolyMap to be queried */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Obtain a pointer to the PolyMap. */
map = (AstPolyMap *) this;
/* Return the result. */
return ( map->ncoeff_i || astGetIterInverse( map ) ) ? 1 : 0;
}
void astInitPolyMapVtab_( AstPolyMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitPolyMapVtab
* Purpose:
* Initialise a virtual function table for a PolyMap.
* Type:
* Protected function.
* Synopsis:
* #include "polymap.h"
* void astInitPolyMapVtab( AstPolyMapVtab *vtab, const char *name )
* Class Membership:
* PolyMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the PolyMap class.
* Parameters:
* vtab
* Pointer to the virtual function table. The components used by
* all ancestral classes will be initialised if they have not already
* been initialised.
* name
* Pointer to a constant null-terminated character string which contains
* the name of the class to which the virtual function table belongs (it
* is this pointer value that will subsequently be returned by the Object
* astClass function).
*-
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstObjectVtab *object; /* Pointer to Object component of Vtab */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
/* Check the local error status. */
if ( !astOK ) return;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialize the component of the virtual function table used by the
parent class. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAPolyMap) to determine if an object belongs
to this class. We can conveniently use the address of the (static)
class_check variable to generate this unique value. */
vtab->id.check = &class_check;
vtab->id.parent = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
vtab->PolyTran = PolyTran;
vtab->ClearIterInverse = ClearIterInverse;
vtab->GetIterInverse = GetIterInverse;
vtab->SetIterInverse = SetIterInverse;
vtab->TestIterInverse = TestIterInverse;
vtab->ClearNiterInverse = ClearNiterInverse;
vtab->GetNiterInverse = GetNiterInverse;
vtab->SetNiterInverse = SetNiterInverse;
vtab->TestNiterInverse = TestNiterInverse;
vtab->ClearTolInverse = ClearTolInverse;
vtab->GetTolInverse = GetTolInverse;
vtab->SetTolInverse = SetTolInverse;
vtab->TestTolInverse = TestTolInverse;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
parent_clearattrib = object->ClearAttrib;
object->ClearAttrib = ClearAttrib;
parent_getattrib = object->GetAttrib;
object->GetAttrib = GetAttrib;
parent_setattrib = object->SetAttrib;
object->SetAttrib = SetAttrib;
parent_testattrib = object->TestAttrib;
object->TestAttrib = TestAttrib;
parent_transform = mapping->Transform;
mapping->Transform = Transform;
mapping->GetTranForward = GetTranForward;
mapping->GetTranInverse = GetTranInverse;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->MapMerge = MapMerge;
/* Declare the destructor and copy constructor. */
astSetDelete( (AstObjectVtab *) vtab, Delete );
astSetCopy( (AstObjectVtab *) vtab, Copy );
/* Declare the class dump function. */
astSetDump( vtab, Dump, "PolyMap", "Polynomial transformation" );
/* If we have just initialised the vtab for the current class, indicate
that the vtab is now initialised, and store a pointer to the class
identifier in the base "object" level of the vtab. */
if( vtab == &class_vtab ) {
class_init = 1;
astSetVtabClassIdentifier( vtab, &(vtab->id) );
}
}
static void IterInverse( AstPolyMap *this, AstPointSet *out, AstPointSet *result,
int *status ){
/*
* Name:
* IterInverse
* Purpose:
* Use an iterative method to evaluate the inverse transformation of a
* PolyMap at a set of output positions.
* Type:
* Private function.
* Synopsis:
* void IterInverse( AstPolyMap *this, AstPointSet *out, AstPointSet *result,
* int *status )
* Description:
* This function transforms a set of PolyMap output positions using
* the inverse transformation of the PolyMap, to generate the corresponding
* input positions. An iterative Newton-Raphson method is used which
* only required the forward transformation of the PolyMap to be deifned.
* Parameters:
* this
* The PolyMap.
* out
* A PointSet holding the PolyMap output positions that are to be
* transformed using the inverse transformation.
* result
* A PointSet into which the generated PolyMap input positions are to be
* stored.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPointSet *work;
AstPointSet **ps_jac;
AstPolyMap **jacob;
double *vec;
double *pb;
double **ptr_work;
double ***ptr_jac;
double *mat;
double **ptr_out;
double **ptr_in;
double *pa;
double det;
double maxerr;
double vlensq;
double xlensq;
double xx;
int *flags;
int *iw;
int fwd;
int icol;
int icoord;
int ipoint;
int irow;
int iter;
int maxiter;
int nconv;
int ncoord;
int npoint;
int sing;
/* Check inherited status */
if( !astOK ) return;
/* Check the PolyMap has equal numbers of inputs and outputs. */
ncoord = astGetNin( this );
if( ncoord != astGetNout( this ) ) {
astError( AST__INTER, "astTransform(%s): Supplied %s has unequal numbers"
" of inputs and outputs and therefore an iterative inverse "
"cannot be used (internal AST Programming errpr).", status,
astGetClass(this), astGetClass(this) );
}
/* Get information about the Jacobian matrix for the forward polynomial
transformation. This matrix is a ncoord X ncoord matrix, in which
element (row=I,col=J) is the rate of change of output coord I with
respect to input coord J, of the supplied PolyMap's forward transformation.
The numerical values of the matrix vary depending on where it is
evaluated within the input space of the PolyMap. For this reason, the
"jacob" variable holds a vector of "ncoord" PolyMaps. The outputs of
each of these PolyMaps corresponds to a single column in the Jacobian
matrix. */
jacob = GetJacobian( this, status );
/* Get the number of points to be transformed. */
npoint = astGetNpoint( out );
/* Get another PointSet to hold intermediate results. */
work = astPointSet( npoint, ncoord, " ", status );
/* See if the PolyMap has been inverted. */
fwd = !astGetInvert( this );
/* Get pointers to the data arrays for all PointSets. Note, here "in" and
"out" refer to inputs and outputs of the PolyMap (i.e. the forward
transformation). These are respectively *outputs* and *inputs* of the
inverse transformation. */
ptr_in = astGetPoints( result ); /* Returned input positions */
ptr_out = astGetPoints( out ); /* Supplied output positions */
ptr_work = astGetPoints( work ); /* Work space */
/* Allocate an array of PointSets to hold the elements of the Jacobian
matrix. */
ptr_jac = astMalloc( sizeof( double ** )*ncoord );
ps_jac = astCalloc( ncoord, sizeof( AstPointSet * ) );
if( astOK ) {
for( icoord = 0; icoord < ncoord; icoord++ ) {
ps_jac[ icoord ] = astPointSet( npoint, ncoord, " ", status );
ptr_jac[ icoord ] = astGetPoints( ps_jac[ icoord ] );
}
}
/* Allocate an array to hold flags indicating if each position has
converged. Initialise it to hold zero at every element. */
flags = astCalloc( npoint, sizeof( int ) );
/* Allocate memory to hold the Jacobian matrix at a single point. */
mat = astMalloc( sizeof( double )*ncoord*ncoord );
/* Allocate memory to hold the offset vector. */
vec = astMalloc( sizeof( double )*ncoord );
/* Allocate memory to hold work space for palDmat. */
iw = astMalloc( sizeof( int )*ncoord );
/* Check pointers can be used safely. */
if( astOK ) {
/* Store the initial guess at the required input positions. We assume initially
that the inverse transformation is a unit mapping, and so we just copy
the supplied outputs positions to the results PointSet holding the
corresponding input positions. */
for( icoord = 0; icoord < ncoord; icoord++ ) {
memcpy( ptr_in[ icoord ], ptr_out[ icoord ], sizeof( double )*npoint );
}
/* Get the maximum number of iterations to perform. */
maxiter = astGetNiterInverse( this );
/* Get the target relative error for the returned input axis values, and
square it. */
maxerr = astGetTolInverse( this );
maxerr *= maxerr;
/* Initialise the number of positions which have reached the required
accuracy. */
nconv = 0;
/* Loop round doing iterations of a Newton-Raphson algorithm, until
all points have achieved the required relative error, or the
maximum number of iterations have been performed. */
for( iter = 0; iter < maxiter && nconv < npoint && astOK; iter++ ) {
/* Use the forward transformation of the supplied PolyMap to transform
the current guesses at the required input positions into the
corresponding output positions. Store the results in the "work"
PointSet. */
(void) astTransform( this, result, fwd, work );
/* Modify the work PointSet so that it holds the offsets from the output
positions produced by the current input position guesses, and the
required output positions. */
for( icoord = 0; icoord < ncoord; icoord++ ) {
pa = ptr_out[ icoord ];
pb = ptr_work[ icoord ];
for( ipoint = 0; ipoint< npoint; ipoint++,pa++,pb++ ) {
if( *pa != AST__BAD && *pb != AST__BAD ){
*pb = *pa - *pb;
} else {
*pb = AST__BAD;
}
}
}
/* Evaluate the elements of the Jacobian matrix at the current input
position guesses. */
for( icoord = 0; icoord < ncoord; icoord++ ) {
(void) astTransform( jacob[ icoord ], result, 1, ps_jac[ icoord ] );
}
/* For each position, we now invert the matrix equation
Dy = Jacobian.Dx
to find a guess at the vector (dx) holding the offsets from the
current input positions guesses to their required values. Loop over all
points. */
for( ipoint = 0; ipoint < npoint; ipoint++ ) {
/* Do not change positions that have already converged. */
if( !flags[ ipoint ] ) {
/* Get the numerical values for the elements of the Jacobian matrix at
the current point. */
pa = mat;
for( irow = 0; irow < ncoord; irow++ ) {
for( icol = 0; icol < ncoord; icol++ ) {
*(pa++) = ptr_jac[ icol ][ irow ][ ipoint ];
}
/* Store the offset from the current output position to the required
output position. */
vec[ irow ] = ptr_work[ irow ][ ipoint ];
}
/* Find the corresponding offset from the current input position to the required
input position. */
palDmat( ncoord, mat, vec, &det, &sing, iw );
/* If the matrix was singular, the input position cannot be evaluated so
store a bad value for it and indicate it has converged. */
if( sing ) {
for( icoord = 0; icoord < ncoord; icoord++ ) {
ptr_in[ icoord ][ ipoint ] = AST__BAD;
}
flags[ ipoint ] = 1;
nconv++;
/* Otherwise, update the input position guess. */
} else {
vlensq = 0.0;
xlensq = 0.0;
pa = vec;
for( icoord = 0; icoord < ncoord; icoord++,pa++ ) {
xx = ptr_in[ icoord ][ ipoint ] + (*pa);
ptr_in[ icoord ][ ipoint ] = xx;
xlensq += xx*xx;
vlensq += (*pa)*(*pa);
}
/* Check for convergence. */
if( vlensq < maxerr*xlensq ) {
flags[ ipoint ] = 1;
nconv++;
}
}
}
}
}
}
/* Free resources. */
vec = astFree( vec );
iw = astFree( iw );
mat = astFree( mat );
flags = astFree( flags );
work = astAnnul( work );
if( ps_jac ) {
for( icoord = 0; icoord < ncoord; icoord++ ) {
ps_jac[ icoord ] = astAnnul( ps_jac[ icoord ] );
}
ps_jac = astFree( ps_jac );
}
ptr_jac = astFree( ptr_jac );
}
static void LMFunc1D( double *p, double *hx, int m, int n, void *adata ){
/*
* Name:
* LMFunc1D
* Purpose:
* Evaluate a test 1D polynomal.
* Type:
* Private function.
* Synopsis:
* void LMFunc1D( double *p, double *hx, int m, int n, void *adata )
* Description:
* This function finds the residuals implied by a supplied set of
* candidate polynomial coefficients. Each residual is a candidate
* polynomial evaluated at one of the sample points, minus the
* supplied target value for the polynomial at that test point.
*
* The minimisation process minimises the sum of the squared residuals.
* Parameters:
* p
* An array of "m" coefficients for the candidate polynomial. The
* coefficients are ordered C0, C1, C2, etc.
* hx
* An array in which to return the "n" residuals. The residual at
* sample "k" is returned in element (k).
* m
* The length of the "p" array. This should be equal to order.
* n
* The length of the "hx" array. This should be equal to nsamp.
* adata
* Pointer to a structure holding the sample positions and values,
* and other information.
*/
/* Local Variables: */
LevMarData *data;
double *px1;
double *py;
double *vp;
double *vr;
double res;
int k;
int w1;
/* Get a pointer to the data structure. */
data = (LevMarData *) adata;
/* Initialise a pointer to the current returned residual value. */
vr = hx;
/* Initialise a pointer to the sampled Y values for the polynomial output. */
py = data->y[ 0 ];
/* Initialise a pointer to the powers of the input X values at the curent
(i.e. first) sample. */
px1 = data->xp1;
/* Loop over the index of the sample to which this residual refers. */
for( k = 0; k < data->nsamp; k++ ) {
/* Initialise a pointer to the first coefficient (the constant term) for the
polynomial output coordinate. */
vp = p;
/* Initialise this residual to hold the sampled Y value. Increment the
pointer to the next sampled value for the current polynomial output. */
res = -( *(py++) );
/* Loop over the coefficients. */
for( w1 = 0; w1 < data->order; w1++ ) {
/* Increment the residual by the value of the current term Cw1*(x1^w1).
Increment the pointer to the next coefficient (C). Also increment the
pointer to the next higher power of X1. */
res += ( *(vp++) )*( *(px1++) );
}
/* Store the complete residual in the returned array, and increment the
pointer to the next residual. */
*(vr++) = res;
}
}
static void LMFunc2D( double *p, double *hx, int m, int n, void *adata ){
/*
* Name:
* LMFunc2D
* Purpose:
* Evaluate a test 2D polynomal.
* Type:
* Private function.
* Synopsis:
* void LMFunc2D( double *p, double *hx, int m, int n, void *adata )
* Description:
* This function finds the residuals implied by a supplied set of
* candidate polynomial coefficients. Each residual is a candidate
* polynomial (either P1 or P2) evaluated at one of the sample points
* (x1,x2), minus the supplied target value for the polynomial at that
* test point.
*
* The minimisation process minimises the sum of the squared residuals.
* Parameters:
* p
* An array of "m" coefficients for the candidate polynomial. All the
* coefficients for polynomial P1 come first, followed by those for P2.
* Within each each polynomial, the coefficients are order C00, C10,
* C01, C20, C11, C02, C30, C21, C12, C03, etc. So the coefficient
* of (x1^j*x2^k) (=Cjk) for polynomial Pi is stored in element
* [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2] of the "p" array.
* hx
* An array in which to return the "n" residuals. The residual at
* sample "k" for polynomial "i" is returned in element (k + nsamp*i).
* m
* The length of the "p" array. This should be equal to order*(order+1).
* n
* The length of the "hx" array. This should be equal to 2*nsamp.
* adata
* Pointer to a structure holding the sample positions and values,
* and other information.
*/
/* Local Variables: */
LevMarData *data;
double *px1;
double *px10;
double *px20;
double *px2;
double *py;
double *vp0;
double *vp;
double *vr;
double res;
int iout;
int k;
int w12;
int w2;
/* Get a pointer to the data structure. */
data = (LevMarData *) adata;
/* Initialise a pointer to the current returned residual value. */
vr = hx;
/* Initilise a pointer to the first coefficient (the constant term) for the
current (i.e. first) polynomial output coordinate. */
vp0 = p;
/* Loop over each polynomial output coordinate. */
for( iout = 0; iout < 2; iout++ ) {
/* Initialise a pointer to the sampled Y values for the first polynomial
output. */
py = data->y[ iout ];
/* Initialise pointers to the powers of the input X values at the curent
(i.e. first) sample. */
px10 = data->xp1;
px20 = data->xp2;
/* Loop over the index of the sample to which this residual refers. */
for( k = 0; k < data->nsamp; k++ ) {
/* Reset the pointer to the first coefficient (the constant term)
for the current polynomial output coordinate. */
vp = vp0;
/* Initialise this residual to hold the sampled Y value. Increment the
pointer to the next sampled value for the current polynomial output. */
res = -( *(py++) );
/* The w12 value is the sum of the powers of X1 and X2. So w12=0
corresponds to the constant term in the polynomial, and (e.g.) w12=6
corresponds to all the terms for which the sum of the powerss (w1+w2)
is 6. Loop over all possible w12 values. */
for( w12 = 0; w12 < data->order; w12++ ) {
/* The next coeff refers to (x1^w12)*(x2^0). Get pointers to the values
holding x1^w12 and x2^0. */
px1 = px10++;
px2 = px20;
/* Loop over powers of x2. The corresponding power of x1 is "w12-x2", but
is not explicitly needed here. So x1 moves down from w12 to zero, as
w2 moves up from zero to w12. */
for( w2 = 0; w2 <= w12; w2++ ) {
/* Increment the residual by the value of the current term Cw1w2*(x1^w1)*(x2^w2).
Increment the pointer tio the next coefficient (C). Also decrement the
pointer to the next lower power of X1, and increment the pointer to the next
higher power of X2. */
res += ( *(vp++) )*( *(px1--) )*( *(px2++) );
}
}
/* Move on to the x2 powers for the next sample. Don't need to do this
for x1 since px10 is incremented within the above loop. */
px20 += data->order;
/* Store the complete residual in the returned array, and increment the
pointer to the next residual. */
*(vr++) = res;
}
/* Get a pointer to the first coefficient (the constant term) for the
next polynomial output coordinate. */
vp0 += data->order*( 1 + data->order )/2;
}
}
static void LMJacob1D( double *p, double *jac, int m, int n, void *adata ){
/*
* Name:
* LMJacob1D
* Purpose:
* Evaluate the Jacobian matrix of a test 1D polynomal.
* Type:
* Private function.
* Synopsis:
* void LMJacob1D( double *p, double *jac, int m, int n, void *adata )
* Description:
* This function finds the Jacobian matrix that describes the rate of
* change of every residual with respect to every polynomial coefficient.
* Each residual is a candidate polynomial evaluated at one of the sample
* points minus the supplied target value for the polynomial at that test
* point.
*
* For a polynomial the Jacobian matrix is constant (i.e. does not
* depend on the values of the polynomial coefficients). So we only
* evaluate it on the first call.
* Parameters:
* p
* An array of "m" coefficients for the candidate polynomial.
* jac
* An array in which to return the "m*n" elements of the Jacobian
* matrix. The rate of change of residual "r" with respect to
* coefficient "c" is returned in element "r + c*n". The residual
* at sample "k" of polynomial Pi has an "r" index of (k + nsamp*i).
* The coefficient of (x1^j) for polynomial Pi has a "c" index
* of j.
* m
* The length of the "p" array. This should be equal to order.
* n
* The number of residuals. This should be equal to nsamp.
* adata
* Pointer to a structure holding the sample positions and values,
* and other information.
*/
/* Local Variables: */
LevMarData *data;
double *pj;
int k;
int ncof;
int w1;
/* Get a pointer to the data structure. */
data = (LevMarData *) adata;
/* The Jacobian of the residuals with respect to the polynomial
coefficients is constant (i.e. does not depend on the values of the
polynomial coefficients). So we only need to calculate it once. If
this is the first call, calculate the Jacobian and return it in "jac".
otherwise, just return immediately retaining the supplied "jac" values
(which will be the values returned by the previous call to this
function). */
if( data->init_jac ) {
data->init_jac = 0;
/* Store the number of coefficients in one polynomial. */
ncof = data->order;
/* Store a pointer to the next element of the returned Jacobian. */
pj = jac;
/* Loop over all residuals. */
for( k = 0; k < n; k++ ) {
/* Loop over all parameters (i.e. polynomial coefficients). */
for( w1 = 0; w1 < m; w1++ ) {
/* Store the Jacobian. */
*(pj++) = (data->xp1)[ w1 + k*data->order ];
}
}
}
}
static void LMJacob2D( double *p, double *jac, int m, int n, void *adata ){
/*
* Name:
* LMJacob2D
* Purpose:
* Evaluate the Jacobian matrix of a test 2D polynomal.
* Type:
* Private function.
* Synopsis:
* void LMJacob2D( double *p, double *jac, int m, int n, void *adata )
* Description:
* This function finds the Jacobian matrix that describes the rate of
* change of every residual with respect to every polynomial coefficient.
* Each residual is a candidate polynomial (either P1 or P2) evaluated
* at one of the sample points (x1,x2), minus the supplied target value
* for the polynomial at that test point.
*
* For a polynomial the Jacobian matrix is constant (i.e. does not
* depend on the values of the polynomial coefficients). So we only
* evaluate it on the first call.
* Parameters:
* p
* An array of "m" coefficients for the candidate polynomial. All the
* coefficients for polynomial P1 come first, followed by those for P2.
* Within each each polynomial, the coefficients are order C00, C10,
* C01, C20, C11, C02, C30, C21, C12, C03, etc. So the coefficient
* of (x1^j*x2^k) (=Cjk) for polynomial Pi is stored in element
* [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2] of the "p" array.
* jac
* An array in which to return the "m*n" elements of the Jacobian
* matrix. The rate of change of residual "r" with respect to
* coefficient "c" is returned in element "r + c*n". The residual
* at sample "k" of polynomial Pi has an "r" index of (k + nsamp*i).
* The coefficient of (x1^j*x2^k) for polynomial Pi has a "c" index
* of [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2].
* m
* The length of the "p" array. This should be equal to order*(order+1).
* n
* The number of residuals. This should be equal to 2*nsamp.
* adata
* Pointer to a structure holdin gthe sample positions and values,
* and other information.
*/
/* Local Variables: */
LevMarData *data;
double *pj;
int iout;
int k;
int ncof;
int vp;
int vr;
int w1;
int w12;
int w2;
/* Get a pointer to the data structure. */
data = (LevMarData *) adata;
/* The Jacobian of the residuals with respect to the polynomial
coefficients is constant (i.e. does not depend on the values of the
polynomial coefficients). So we only need to calculate it once. If
this is the first call, calculate the Jacobian and return it in "jac".
otherwise, just return immediately retaining the supplied "jac" values
(which will be the values returned by the previous call to this
function). */
if( data->init_jac ) {
data->init_jac = 0;
/* Store the number of coefficients in one polynomial. */
ncof = data->order*( 1 + data->order )/2;
/* Store a pointer to the next element of the returned Jacobian. */
pj = jac;
/* Loop over all residuals. */
for( vr = 0; vr < n; vr++ ) {
/* Calculate the polynomial output index, and sample index, that creates
the current residual. */
iout = vr/data->nsamp;
k = vr - iout*data->nsamp;
/* Loop over all parameters (i.e. polynomial coefficients). */
for( vp = 0; vp < m; vp++ ) {
/* If this coefficient is not used in the creation of the current
polynomial output value, then the Jacobian value is zero. */
if( vp/ncof != iout ) {
*(pj++) = 0.0;
/* Otherwise, get the powers of the two polynomial inputs, to which
the current coefficient relates. */
} else {
w12 = ( -1.0 + sqrt( 1.0 + 8.0*(vp - iout*ncof) ) )/2.0;
w2 = vp - iout*ncof - w12*( w12 + 1 )/2;
w1 = w12 - w2;
/* Store the Jacobian. */
*(pj++) = (data->xp1)[ w1 + k*data->order ]*
(data->xp2)[ w2 + k*data->order ];
}
}
}
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* PolyMap member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstPolyMap *this;
int ic;
int result;
int nc;
/* Initialise */
result = 0;
/* Check the supplied pointer is not NULL. */
if( !this_object ) return result;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( this->jacobian ) {
nc = astGetNin( this );
for( ic = 0; ic < nc && result; ic++ ) {
result = astManageLock( (this->jacobian)[ ic ], mode,
extra, fail );
}
}
return result;
}
#endif
static int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list, int *status )
* Class Membership:
* PolyMap method (over-rides the protected astMapMerge method
* inherited from the Mapping class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated PolyMap in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated PolyMap with a Mapping which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated PolyMap which is to be merged with
* its neighbours. This should be a cloned copy of the PolyMap
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* PolyMap it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated PolyMap resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstPolyMap *pmap0; /* Nominated PolyMap */
AstPolyMap *pmap1; /* Neighbouring PolyMap */
int i; /* Index of neighbour */
int iax_in; /* Index of input coordinate */
int iax_out; /* Index of output coordinate */
int ico; /* Index of coefficient */
int inv0; /* Supplied Invert flag for nominated PolyMap */
int inv1; /* Supplied Invert flag for neighbouring PolyMap */
int nc; /* Number of coefficients */
int nin; /* Number of input coordinates for nominated PolyMap */
int nout; /* Number of output coordinates for nominated PolyMap */
int ok; /* Are PolyMaps equivalent? */
int result; /* Result value to return */
int swap0; /* Swap inputs and outputs for nominated PolyMap? */
int swap1; /* Swap inputs and outputs for neighbouring PolyMap? */
/* Initialise. */
result = -1;
/* Check the global error status. */
if ( !astOK ) return result;
/* Save a pointer to the nominated PolyMap. */
pmap0 = (AstPolyMap *) ( *map_list )[ where ];
/* The only simplification which can currently be performed is to merge a PolyMap
with its own inverse. This can only be done in series. Obviously,
there are potentially other simplications which could be performed, but
time does not currently allow these to be coded. */
if( series ) {
/* Set a flag indicating if "input" and "output" needs to be swapped for
the nominated PolyMap. */
inv0 = ( *invert_list )[ where ];
swap0 = ( inv0 != astGetInvert( pmap0 ) );
/* Get the number of inputs and outputs to the nominated PolyMap. */
nin = !swap0 ? astGetNin( pmap0 ) : astGetNout( pmap0 );
nout = !swap0 ? astGetNout( pmap0 ) : astGetNin( pmap0 );
/* Check each neighbour. */
for( i = where - 1; i <= where + 1; i += 2 ) {
/* Continue with the next pass if the neighbour does not exist. */
if( i < 0 || i >= *nmap ) continue;
/* Continue with the next pass if this neighbour is not a PermMap. */
if( strcmp( "PolyMap", astGetClass( ( *map_list )[ i ] ) ) ) continue;
/* Get a pointer to it. */
pmap1 = (AstPolyMap *) ( *map_list )[ i ];
/* Check it is used in the opposite direction to the nominated PolyMap. */
if( ( *invert_list )[ i ] == ( *invert_list )[ where ] ) continue;
/* Set a flag indicating if "input" and "output" needs to be swapped for
the neighbouring PolyMap. */
inv1 = ( *invert_list )[ i ];
swap1 = ( inv1 != astGetInvert( pmap1 ) );
/* Check the number of inputs and outputs are equal to the nominated
PolyMap. */
if( astGetNin( pmap1 ) != (!swap1 ? nin : nout ) &&
astGetNout( pmap1 ) != (!swap1 ? nout : nin ) ) continue;
/* Check the forward coefficients are equal. */
ok = 1;
for( iax_out = 0; iax_out < nout && ok; iax_out++ ) {
nc = pmap1->ncoeff_f[ iax_out ];
if( nc != pmap0->ncoeff_f[ iax_out ] ) continue;
for( ico = 0; ico < nc && ok; ico++ ) {
if( !EQUAL( pmap1->coeff_f[ iax_out ][ ico ],
pmap0->coeff_f[ iax_out ][ ico ] ) ){
ok = 0;
} else {
for( iax_in = 0; iax_in < nin && ok; iax_in++ ) {
ok = ( pmap1->power_f[ iax_out ][ ico ][ iax_in ] ==
pmap0->power_f[ iax_out ][ ico ][ iax_in ] );
}
}
}
}
if( !ok ) continue;
/* Check the inverse coefficients are equal. */
ok = 1;
for( iax_in = 0; iax_in < nin && ok; iax_in++ ) {
nc = pmap1->ncoeff_i[ iax_in ];
if( nc != pmap0->ncoeff_i[ iax_in ] ) continue;
for( ico = 0; ico < nc && ok; ico++ ) {
if( !EQUAL( pmap1->coeff_i[ iax_in ][ ico ],
pmap0->coeff_i[ iax_in ][ ico ] ) ){
ok = 0;
} else {
for( iax_out = 0; iax_out < nout && ok; iax_out++ ) {
ok = ( pmap1->power_i[ iax_in ][ ico ][ iax_out ] ==
pmap0->power_i[ iax_in ][ ico ][ iax_out ] );
}
}
}
}
if( !ok ) continue;
/* If we get this far, then the nominated PolyMap and the current
neighbour cancel each other out, so replace each by a UnitMap. */
(void) astAnnul( pmap0 );
(void) astAnnul( pmap1 );
if( i < where ) {
( *map_list )[ where ] = (AstMapping *) astUnitMap( nout, "", status );
( *map_list )[ i ] = (AstMapping *) astUnitMap( nout, "", status );
( *invert_list )[ where ] = 0;
( *invert_list )[ i ] = 0;
result = i;
} else {
( *map_list )[ where ] = (AstMapping *) astUnitMap( nin, "", status );
( *map_list )[ i ] = (AstMapping *) astUnitMap( nin, "", status );
( *invert_list )[ where ] = 0;
( *invert_list )[ i ] = 0;
result = where;
}
/* Leave the loop. */
break;
}
}
/* Return the result. */
return result;
}
static AstPolyMap *PolyTran( AstPolyMap *this, int forward, double acc,
double maxacc, int maxorder, const double *lbnd,
const double *ubnd, int *status ){
/*
*++
* Name:
c astPolyTran
f AST_POLYTRAN
* Purpose:
* Fit a PolyMap inverse or forward transformation.
* Type:
* Public function.
* Synopsis:
c #include "polymap.h"
c AstPolyMap *astPolyTran( AstPolyMap *this, int forward, double acc,
c double maxacc, int maxorder, const double *lbnd,
c const double *ubnd )
f RESULT = AST_POLYTRAN( THIS, FORWARD, ACC, MAXACC, MAXORDER, LBND,
f UBND, STATUS )
* Class Membership:
* PolyMap method.
* Description:
* This function creates a new PolyMap which is a copy of the supplied
* PolyMap, in which a specified transformation (forward or inverse)
* has been replaced by a new polynomial transformation. The
* coefficients of the new transformation are estimated by sampling
* the other transformation and performing a least squares polynomial
* fit in the opposite direction to the sampled positions and values.
*
* This method can only be used on (1-input,1-output) or (2-input,2-output)
* PolyMaps.
*
* The transformation to create is specified by the
c "forward" parameter.
f FORWARD parameter.
* In what follows "X" refers to the inputs of the PolyMap, and "Y" to
* the outputs of the PolyMap. The forward transformation transforms
* input values (X) into output values (Y), and the inverse transformation
* transforms output values (Y) into input values (X). Within a PolyMap,
* each transformation is represented by an independent set of
* polynomials, P_f or P_i: Y=P_f(X) for the forward transformation and
* X=P_i(Y) for the inverse transformation.
*
c The "forward"
f The FORWARD
* parameter specifies the transformation to be replaced. If it is
c non-zero,
f is .TRUE.,
* a new forward transformation is created
* by first finding the input values (X) using the inverse transformation
* (which must be available) at a regular grid of points (Y) covering a
* rectangular region of the PolyMap's output space. The coefficients of
* the required forward polynomial, Y=P_f(X), are chosen in order to
* minimise the sum of the squared residuals between the sampled values
* of Y and P_f(X).
*
c If "forward" is zero (probably the most likely case),
f If FORWARD is .FALSE. (probably the most likely case),
* a new inverse transformation is created by
* first finding the output values (Y) using the forward transformation
* (which must be available) at a regular grid of points (X) covering a
* rectangular region of the PolyMap's input space. The coefficients of
* the required inverse polynomial, X=P_i(Y), are chosen in order to
* minimise the sum of the squared residuals between the sampled values
* of X and P_i(Y).
*
* This fitting process is performed repeatedly with increasing
* polynomial orders (starting with linear) until the target
* accuracy is achieved, or a specified maximum order is reached. If
* the target accuracy cannot be achieved even with this maximum-order
* polynomial, the best fitting maximum-order polynomial is returned so
* long as its accuracy is better than
c "maxacc".
f MAXACC.
* If it is not, an error is reported.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the original Mapping.
c forward
f FORWARD = LOGICAL (Given)
c If non-zero,
f If .TRUE.,
* the forward PolyMap transformation is replaced. Otherwise the
* inverse transformation is replaced.
c acc
f ACC = DOUBLE (Given)
* The target accuracy, expressed as a geodesic distance within
* the PolyMap's input space (if
c "forward" is zero) or output space (if "forward" is non-zero).
f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.).
c maxacc
f MAXACC = DOUBLE (Given)
* The maximum allowed accuracy for an acceptable polynomial,
* expressed as a geodesic distance within the PolyMap's input
* space (if
c "forward" is zero) or output space (if "forward" is non-zero).
f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.).
c maxorder
f MAXORDER = INTEGER (Given)
* The maximum allowed polynomial order. This is one more than the
* maximum power of either input axis. So for instance, a value of
* 3 refers to a quadratic polynomial. Note, cross terms with total
* powers greater than or equal to
c maxorder
f MAXORDER
* are not inlcuded in the fit. So the maximum number of terms in
* each of the fitted polynomials is
c maxorder*(maxorder+1)/2.
f MAXORDER*(MAXORDER+1)/2.
c lbnd
f LBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an
f An
* array holding the lower bounds of a rectangular region within
* the PolyMap's input space (if
c "forward" is zero) or output space (if "forward" is non-zero).
f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.).
* The new polynomial will be evaluated over this rectangle. The
* length of this array should equal the value of the PolyMap's Nin
* or Nout attribute, depending on
c "forward".
f FORWARD.
c ubnd
f UBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an
f An
* array holding the upper bounds of a rectangular region within
* the PolyMap's input space (if
c "forward" is zero) or output space (if "forward" is non-zero).
f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.).
* The new polynomial will be evaluated over this rectangle. The
* length of this array should equal the value of the PolyMap's Nin
* or Nout attribute, depending on
c "forward".
f FORWARD.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astPolyTran()
f AST_POLYTRAN = INTEGER
* A pointer to the new PolyMap.
c A NULL pointer
f AST__NULL
* will be returned if the fit fails to achieve the accuracy
* specified by
c "maxacc",
f MAXACC,
* but no error will be reported.
* Notes:
* - This function can only be used on 1D or 2D PolyMaps which have
* the same number of inputs and outputs.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
*/
/* Local Variables: */
AstPolyMap *result;
int ok;
/* Initialise. */
result = NULL;
/* Check the inherited status. */
if ( !astOK ) return result;
/* Take a copy of the supplied PolyMap. */
result = astCopy( this );
/* Replace the required transformation. */
ok = ReplaceTransformation( result, forward, acc, maxacc, maxorder, lbnd,
ubnd, status );
/* If an error occurred, or the fit was not good enough, annul the returned
PolyMap. */
if ( !ok || !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static int ReplaceTransformation( AstPolyMap *this, int forward, double acc,
double maxacc, int maxorder, const double *lbnd,
const double *ubnd, int *status ){
/*
* Name:
* ReplaceTransformation
* Purpose:
* Create a new inverse or forward transformation for a PolyMap.
* Type:
* Private function.
* Synopsis:
* int ReplaceTransformation( AstPolyMap *this, int forward, double acc,
* double maxacc, int maxorder, const double *lbnd,
* const double *ubnd, int *status )
* Description:
* This function creates a new forward or inverse transformation for
* the supplied PolyMap (replacing any existing transformation), by
* sampling the other transformation and performing a least squares
* polynomial fit to the sample positions and values.
*
* The transformation to create is specified by the "forward" parameter.
* In what follows "X" refers to the inputs of the PolyMap, and "Y" to
* the outputs of the PolyMap. The forward transformation transforms
* input values (X) into output values (Y), and the inverse transformation
* transforms output values (Y) into input values (X). Within a PolyMap,
* each transformation is represented by an independent set of
* polynomials: Y=P_f(X) for the forward transformation and X=P_i(Y)
* for the inverse transformation.
*
* If "forward" is zero then a new inverse transformation is created by
* first finding the output values (Y) using the forward transformation
* (which must be available) at a regular grid of points (X) covering a
* rectangular region of the PolyMap's input space. The coefficients of
* the required inverse polynomial, X=P_i(Y), are chosen in order to
* minimise the sum of the squared residuals between the sampled values
* of X and P_i(Y).
*
* If "forward" is non-zero then a new forward transformation is created
* by first finding the input values (X) using the inverse transformation
* (which must be available) at a regular grid of points (Y) covering a
* rectangular region of the PolyMap's output space. The coefficients of
* the required forward polynomial, Y=P_f(X), are chosen in order to
* minimise the sum of the squared residuals between the sampled values
* of Y and P_f(X).
*
* This fitting process is performed repeatedly with increasing
* polynomial orders (starting with linear) until the target
* accuracy is achieved, or a specified maximum order is reached. If
* the target accuracy cannot be achieved even with this maximum-order
* polynomial, the best fitting maximum-order polynomial is returned so
* long as its accuracy is better than "maxacc".
* Parameters:
* this
* The PolyMap.
* forward
* If non-zero, then the forward PolyMap transformation is
* replaced. Otherwise the inverse transformation is replaced.
* acc
* The target accuracy, expressed as a geodesic distance within
* the PolyMap's input space (if "forward" is zero) or output
* space (if "forward" is non-zero).
* maxacc
* The maximum allowed accuracy for an acceptable polynomial,
* expressed as a geodesic distance within the PolyMap's input
* space (if "forward" is zero) or output space (if "forward" is
* non-zero).
* maxorder
* The maximum allowed polynomial order. This is one more than the
* maximum power of either input axis. So for instance, a value of
* 3 refers to a quadratic polynomial. Note, cross terms with total
* powers greater than or equal to maxorder are not inlcuded in the
* fit. So the maximum number of terms in each of the fitted polynomials
* is maxorder*(maxorder+1)/2.
* lbnd
* An array holding the lower bounds of a rectangular region within
* the PolyMap's input space (if "forward" is zero) or output
* space (if "forward" is non-zero). The new polynomial will be
* evaluated over this rectangle.
* ubnd
* An array holding the upper bounds of a rectangular region within
* the PolyMap's input space (if "forward" is zero) or output
* space (if "forward" is non-zero). The new polynomial will be
* evaluated over this rectangle.
* status
* Pointer to the inherited status variable.
* Returned Value:
* - Non-zero if a fit was performed succesfully, to at least the
* maximum allowed accuracy. Zero if the fit failed, or an error
* occurred.
* Notes:
* - No error is reported if the fit fails to achieve the required
* maximum accuracy.
* - An error is reported if the transformation that is not being
* replaced is not defined.
* - An error is reported if the PolyMap does not have equal numbers
* of inputs and outputs.
* - An error is reported if the PolyMap has more than 2 inputs or outputs.
*/
/* Local Variables: */
double **table;
double *cofs;
double racc;
double scales[ 4 ];
int ndim;
int ncof;
int nsamp;
int order;
int result;
/* Check inherited status */
if( !astOK ) return 0;
/* Check the PolyMap can be used. */
ndim = astGetNin( this );
if( astGetNout( this ) != ndim && astOK ) {
astError( AST__BADNI, "astPolyTran(%s): Supplied %s has "
"different number of inputs (%d) and outputs (%d).",
status, astGetClass( this ), astGetClass( this ), ndim,
astGetNout( this ) );
} else if( ndim > 2 && astOK ) {
astError( AST__BADNI, "astPolyTran(%s): Supplied %s has "
"too many inputs and outputs (%d) - must be 1 or 2.",
status, astGetClass( this ), astGetClass( this ), ndim );
}
if( forward != astGetInvert( this ) ){
if( ! this->ncoeff_i && astOK ) {
astError( AST__NODEF, "astPolyTran(%s): Supplied %s has "
"no inverse transformation.", status, astGetClass( this ),
astGetClass( this ) );
}
} else {
if( ! this->ncoeff_f && astOK ) {
astError( AST__NODEF, "astPolyTran(%s): Supplied %s has "
"no forward transformation.", status, astGetClass( this ),
astGetClass( this ) );
}
}
/* Check the bounds can be used. */
if( lbnd[ 0 ] >= ubnd[ 0 ] && astOK ) {
astError( AST__NODEF, "astPolyTran(%s): Supplied upper "
"bound for the first axis (%g) is less than or equal to the "
"supplied lower bound (%g).", status, astGetClass( this ),
lbnd[ 0 ], ubnd[ 0 ] );
} else if( ndim == 2 && lbnd[ 1 ] >= ubnd[ 1 ] && astOK ) {
astError( AST__NODEF, "astPolyTran(%s): Supplied upper "
"bound for the second axis (%g) is less than or equal to the "
"supplied lower bound (%g).", status, astGetClass( this ),
lbnd[ 1 ], ubnd[ 1 ] );
}
/* Initialise pointer to work space. */
table = NULL;
/* Loop over increasing polynomial orders until the required accuracy is
achieved, up to the specified maximum order. The "order" value is one more
than the maximum power in the polynomial (so a quadratic has "order" 3). */
if( maxorder < 2 ) maxorder = 2;
for( order = 2; order <= maxorder; order++ ) {
/* First do 2D PolyMaps. */
if( ndim == 2 ) {
/* Sample the requested polynomial transformation at a grid of points. This
grid covers the user-supplied region, using 2*order points on each
axis. If the PolyMap is 1D, then it will be treated as a 2D polynomial
in which the second output is a unit transformation. */
table = SamplePoly2D( this, !forward, table, lbnd, ubnd, 2*order,
&nsamp, scales, status );
/* Fit the polynomial. Always fit a linear polynomial ("order" 2) to any
dummy second axis. If succesful, replace the PolyMap transformation
and break out of the order loop. */
cofs = FitPoly2D( nsamp, acc, order, table, scales, &ncof, &racc,
status );
/* Now do 1D PolyMaps. */
} else {
table = SamplePoly1D( this, !forward, table, lbnd[ 0 ], ubnd[ 0 ],
2*order, &nsamp, scales, status );
cofs = FitPoly1D( nsamp, acc, order, table, scales, &ncof, &racc,
status );
}
/* If the fit was succesful, replace the PolyMap transformation and break
out of the order loop. */
if( cofs && ( racc < acc || ( racc < maxacc && order == maxorder ) ) ) {
StoreArrays( this, forward, ncof, cofs, status );
break;
} else {
cofs = astFree( cofs );
}
}
/* If no fit was produced, return zero. */
result = cofs ? 1 : 0;
/* Free resources. */
cofs = astFree( cofs );
table = astFreeDouble( table );
/* Return the result. */
return result;
}
static double **SamplePoly1D( AstPolyMap *this, int forward, double **table,
double lbnd, double ubnd, int npoint, int *nsamp,
double scales[2], int *status ){
/*
* Name:
* SamplePoly1D
* Purpose:
* Create a table of input and output positions for a 1D PolyMap.
* Type:
* Private function.
* Synopsis:
* double **SamplePoly1D( AstPolyMap *this, int forward, double **table,
* double lbnd, double ubnd, int npoint, int *nsamp,
* double scales[2], int *status )
* Description:
* This function creates a table containing samples of the requested
* polynomial transformation at a grid of input points. This grid covers
* the user-supplied region, using "npoint" points.
* Parameters:
* this
* The PolyMap.
* forward
* If non-zero, then the forward PolyMap transformation is sampled.
* Otherwise the inverse transformation is sampled.
* table
* Pointer to a previous table created by this function, which is
* to be re-used, or NULL.
* lbnd
* The lower bounds of the region within the PolyMap's 1D input space
* (if "forward" is non-zero) or output space (if "forward" is zero).
* The new polynomial will be evaluated over this region.
* ubnd
* The upper bounds of the region within the PolyMap's 1D input space
* (if "forward" is non-zero) or output space (if "forward" is zero).
* The new polynomial will be evaluated over this region.
* npoint
* The number of points to use.
* nsamp
* Address of an int in which to return the total number of samples
* in the returned table.
* scales
* Array in which to return the scaling factors for the two columns
* of the returned table. Multiplying the returned table values by
* the scale factor produces PolyMap input or output axis values.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to an array of 2 pointers. Each of these pointers points
* to an array of "nsamp" doubles, being the sampled values for y1
* and x1 in that order. Here x1 is the input value for the sampled
* transformation (these are spaced on the regular grid specified
* by lbnd, ubnd and npoint), and y1 is the output position produced
* by the sampled transformation. The returned values are scaled so
* that each column has an RMS value of 1.0. The scaling factors that
* convert scaled values into original values are returned in "scales".
* The returned pointer should be freed using astFreeDouble when no
* longer needed.
*/
/* Local Variables: */
AstPointSet *ps1;
AstPointSet *ps2;
double **result;
double *p0;
double *p1;
double *ptr1[ 1 ];
double *ptr2[ 1 ];
double delta0;
double rms;
double sum;
double val0;
int i;
int icol;
/* Initialise returned value */
result = table;
*nsamp = 0;
/* Check inherited status */
if( !astOK ) return result;
/* Ensure we have a table of the correct size. */
*nsamp = npoint;
if( !result ) result = astCalloc( 2, sizeof( double * ) );
if( result ) {
for( i = 0; i < 2; i++ ) {
result[ i ] = astRealloc( result[ i ] , (*nsamp)*sizeof( double ) );
}
}
/* Work out the step sizes for the grid. */
delta0 = ( ubnd - lbnd )/( npoint - 1 );
/* Create a PointSet to hold the grid of input positions. Use column 1
of the table to hold the PointSet values. */
ps1 = astPointSet( *nsamp, 1, " ", status );
ptr1[ 0 ] = result[ 1 ];
astSetPoints( ps1, ptr1 );
/* Create a PointSet to hold the grid of output positions. Use column 0
of the table to hold the PointSet values. */
ps2 = astPointSet( *nsamp, 1, " ", status );
ptr2[ 0 ] = result[ 0 ];
astSetPoints( ps2, ptr2 );
if( astOK ) {
/* Calculate the grid of input positions and store in the PointSet and
therefore also in the returned table. */
val0 = lbnd;
p0 = ptr1[ 0 ];
for( i = 0; i < npoint; i++ ) {
*(p0++) = val0;
val0 += delta0;
}
/* Transform the input grid to get the output grid. */
(void) astTransform( this, ps1, forward, ps2 );
/* Scale each column in turn. */
for( icol = 0; icol < 2; icol++ ) {
/* Find the RMS of the values in the column. */
sum = 0.0;
p0 = result[ icol ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 );
rms = sqrt( sum/(*nsamp) );
/* Divide the table values by the RMS. */
p0 = result[ icol ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) *p0 /= rms;
/* Return the RMS as the scale factor. */
scales[ icol ] = rms;
}
}
/* Free resources */
ps1 = astAnnul( ps1 );
ps2 = astAnnul( ps2 );
/* If an error occurred, free the returned array. */
if( !astOK ) result = astFreeDouble( result );
/* Return a pointer to the table. */
return result;
}
static double **SamplePoly2D( AstPolyMap *this, int forward, double **table,
const double *lbnd, const double *ubnd, int npoint,
int *nsamp, double scales[4], int *status ){
/*
* Name:
* SamplePoly2D
* Purpose:
* Create a table of input and output positions for a 2D PolyMap.
* Type:
* Private function.
* Synopsis:
* double **SamplePoly2D( AstPolyMap *this, int forward, double **table,
* const double *lbnd, const double *ubnd, int npoint,
* int *nsamp, double scales[4], int *status )
* Description:
* This function creates a table containing samples of the requested
* polynomial transformation at a grid of input points. This grid covers
* the user-supplied region, using "npoint" points on each axis.
* Parameters:
* this
* The PolyMap.
* forward
* If non-zero, then the forward PolyMap transformation is sampled.
* Otherwise the inverse transformation is sampled.
* table
* Pointer to a previous table created by this function, which is
* to be re-used, or NULL.
* lbnd
* An array holding the lower bounds of a rectangular region within
* the PolyMap's input space (if "forward" is non-zero) or output
* space (if "forward" is zero). The new polynomial will be
* evaluated over this rectangle.
* ubnd
* An array holding the upper bounds of a rectangular region within
* the PolyMap's input space (if "forward" is non-zero) or output
* space (if "forward" is zero). The new polynomial will be
* evaluated over this rectangle.
* npoint
* The number of points along each edge of the grid.
* nsamp
* Address of an int in which to return the total number of samples
* in the returned table.
* scales
* Array in which to return the scaling factors for the four
* columns of the returned table. Multiplying the returned table
* values by the scale factor produces PolyMap input or output axis
* values.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to an array of 4 pointers. Each of these pointers points
* to an array of "nsamp" doubles, being the sampled values for y1,
* y2, x1 or x2 in that order. Here (x1,x2) are the input values
* for the sampled transformation (these are spaced on the regular
* grid specified by lbnd, ubnd and npoint), and (y1,y2) are the
* output positions produced by the sampled transformation. The
* returned cvalues are scaled so that each column has an RMS value
* of 1.0. The scaling factors that convert scaled values into
* original values are returned in "scales". The returned pointer
* should be freed using astFreeDouble when no longer needed.
*/
/* Local Variables: */
AstPointSet *ps1;
AstPointSet *ps2;
double **result;
double *p0;
double *p1;
double *ptr1[ 2 ];
double *ptr2[ 2 ];
double delta1;
double delta0;
double rms;
double sum;
double val0;
double val1;
int i;
int icol;
int j;
/* Initialise returned value */
result = table;
*nsamp = 0;
/* Check inherited status */
if( !astOK ) return result;
/* Ensure we have a table of the correct size. */
*nsamp = npoint*npoint;
if( !result ) result = astCalloc( 4, sizeof( double * ) );
if( result ) {
for( i = 0; i < 4; i++ ) {
result[ i ] = astRealloc( result[ i ] , (*nsamp)*sizeof( double ) );
}
}
/* Work out the step sizes for the grid. */
delta0 = ( ubnd[ 0 ] - lbnd[ 0 ] )/( npoint - 1 );
delta1 = ( ubnd[ 1 ] - lbnd[ 1 ] )/( npoint - 1 );
/* Create a PointSet to hold the grid of input positions. Use columns 2
and 3 of the table to hold the PointSet values. */
ps1 = astPointSet( *nsamp, 2, " ", status );
ptr1[ 0 ] = result[ 2 ];
ptr1[ 1 ] = result[ 3 ];
astSetPoints( ps1, ptr1 );
/* Create a PointSet to hold the grid of output positions. Use columns 0
and 1 of the table to hold the PointSet values. */
ps2 = astPointSet( *nsamp, 2, " ", status );
ptr2[ 0 ] = result[ 0 ];
ptr2[ 1 ] = result[ 1 ];
astSetPoints( ps2, ptr2 );
if( astOK ) {
/* Calculate the grid of input positions and store in the PointSet and
therefore also in the returned table. */
val0 = lbnd[ 0 ];
p0 = ptr1[ 0 ];
p1 = ptr1[ 1 ];
for( i = 0; i < npoint; i++ ) {
val1 = lbnd[ 1 ];
for( j = 0; j < npoint; j++ ) {
*(p0++) = val0;
*(p1++) = val1;
val1 += delta1;
}
val0 += delta0;
}
/* Transform the input grid to get the output grid. */
(void) astTransform( this, ps1, forward, ps2 );
/* Scale each pair of columns in turn. Use the ssame scale factor for
each axis in order to ensure an isotropic metric. */
for( icol = 0; icol < 4; icol += 2 ) {
/* Find the RMS of the values in the two columns. */
sum = 0.0;
p0 = result[ icol ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 );
p0 = result[ icol + 1 ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 );
rms = sqrt( sum/(2*(*nsamp)) );
/* Divide the table values by the RMS. */
p0 = result[ icol ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) *p0 /= rms;
p0 = result[ icol + 1 ];
p1 = p0 + (*nsamp);
for( ; p0 < p1; p0++ ) *p0 /= rms;
/* Return the RMS as the scale factor. */
scales[ icol ] = rms;
scales[ icol + 1 ] = rms;
}
}
/* Free resources */
ps1 = astAnnul( ps1 );
ps2 = astAnnul( ps2 );
/* If an error occurred, free the returned array. */
if( !astOK ) result = astFreeDouble( result );
/* Return a pointer to the table. */
return result;
}
static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
/*
* Name:
* SetAttrib
* Purpose:
* Set an attribute value for a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* void SetAttrib( AstObject *this, const char *setting )
* Class Membership:
* PolyMap member function (over-rides the astSetAttrib protected
* method inherited from the Mapping class).
* Description:
* This function assigns an attribute value for a PolyMap, the
* attribute and its value being specified by means of a string of
* the form:
*
* "attribute= value "
*
* Here, "attribute" specifies the attribute name and should be in
* lower case with no white space present. The value to the right
* of the "=" should be a suitable textual representation of the
* value to be assigned and this will be interpreted according to
* the attribute's data type. White space surrounding the value is
* only significant for string attributes.
* Parameters:
* this
* Pointer to the PolyMap.
* setting
* Pointer to a null-terminated string specifying the new attribute
* value.
*/
/* Local Variables: */
AstPolyMap *this; /* Pointer to the PolyMap structure */
double dval; /* Floating point attribute value */
int ival; /* Integer attribute value */
int len; /* Length of setting string */
int nc; /* Number of characters read by astSscanf */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Obtain the length of the setting string. */
len = (int) strlen( setting );
/* Test for each recognised attribute in turn, using "astSscanf" to parse
the setting string and extract the attribute value (or an offset to
it in the case of string values). In each case, use the value set
in "nc" to check that the entire string was matched. Once a value
has been obtained, use the appropriate method to set it. */
/* IterInverse. */
/* ------------ */
if ( nc = 0,
( 1 == astSscanf( setting, "iterinverse= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetIterInverse( this, ival );
/* NiterInverse. */
/* ------------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "niterinverse= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetNiterInverse( this, ival );
/* TolInverse. */
/* ----------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "tolinverse= %lg %n", &dval, &nc ) )
&& ( nc >= len ) ) {
astSetTolInverse( this, dval );
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
(*parent_setattrib)( this_object, setting, status );
}
}
static void StoreArrays( AstPolyMap *this, int forward, int ncoeff,
const double *coeff, int *status ){
/*
* Name:
* StoreArrays
* Purpose:
* Store the dynamic arrays for a single transformation within a PolyMap
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* void StoreArrays( AstPolyMap *this, int forward, int ncoeff,
* const double *coeff, int *status )
* Class Membership:
* PolyMap initialiser.
* Description:
* This function sets up the arrays within a PolyMap structure that
* describes either the forward or inverse transformation.
* Parameters:
* this
* The PolyMap.
* forward
* If non-zero, replace the forward transformation. Otherwise,
* replace the inverse transformation.
* ncoeff
* The number of non-zero coefficients necessary to define the
* specified transformation of the PolyMap. If zero is supplied, the
* transformation will be undefined.
* coeff
* An array containing "ncof*( 2 + nin )" elements. Each group
* of "2 + nin" adjacent elements describe a single coefficient of
* the transformation. Within each such group, the first
* element is the coefficient value; the next element is the
* integer index of the PolyMap output which uses the coefficient
* within its defining polynomial (the first output has index 1);
* the remaining elements of the group give the integer powers to
* use with each input coordinate value (powers must not be
* negative).
* status
* Pointer to inherited status.
*/
/* Local Variables: */
const double *group; /* Pointer to start of next coeff. description */
int *pows; /* Pointer to powers for current coeff. */
int gsize; /* Length of each coeff. description */
int i; /* Loop count */
int ico; /* Index of next coeff. for current input or output */
int iin; /* Input index extracted from coeff. description */
int iout; /* Output index extracted from coeff. description */
int j; /* Loop count */
int nin; /* Number of inputs */
int nout; /* Number of outputs */
int pow; /* Power extracted from coeff. description */
/* Check the global status. */
if ( !astOK ) return;
/* Get the number of inputs and outputs. */
nin = astGetNin( this );
nout = astGetNout( this );
/* First Free any existing arrays. */
FreeArrays( this, forward, status );
/* Now initialise the forward transformation, if required. */
if( forward && ncoeff > 0 ) {
/* Create the arrays decribing the forward transformation. */
this->ncoeff_f = astMalloc( sizeof( int )*(size_t) nout );
this->mxpow_f = astMalloc( sizeof( int )*(size_t) nin );
this->power_f = astMalloc( sizeof( int ** )*(size_t) nout );
this->coeff_f = astMalloc( sizeof( double * )*(size_t) nout );
if( astOK ) {
/* Initialise the count of coefficients for each output coordinate to zero. */
for( i = 0; i < nout; i++ ) this->ncoeff_f[ i ] = 0;
/* Initialise max power for each input coordinate to zero. */
for( j = 0; j < nin; j++ ) this->mxpow_f[ j ] = 0;
/* Scan through the supplied forward coefficient array, counting the
number of coefficients which relate to each output. Also find the
highest power used for each input axis. Report errors if any unusable
values are found in the supplied array. */
group = coeff;
gsize = 2 + nin;
for( i = 0; i < ncoeff && astOK; i++, group += gsize ) {
iout = floor( group[ 1 ] + 0.5 );
if( iout < 1 || iout > nout ) {
astError( AST__BADCI, "astInitPolyMap(%s): Forward "
"coefficient %d referred to an illegal output "
"coordinate %d.", status, astGetClass( this ), i + 1,
iout );
astError( AST__BADCI, "This number should be in the "
"range 1 to %d.", status, nout );
break;
}
this->ncoeff_f[ iout - 1 ]++;
for( j = 0; j < nin; j++ ) {
pow = floor( group[ 2 + j ] + 0.5 );
if( pow < 0 ) {
astError( AST__BADPW, "astInitPolyMap(%s): Forward "
"coefficient %d has a negative power (%d) "
"for input coordinate %d.", status,
astGetClass( this ), i + 1, pow, j + 1 );
astError( AST__BADPW, "All powers should be zero or "
"positive." , status);
break;
}
if( pow > this->mxpow_f[ j ] ) this->mxpow_f[ j ] = pow;
}
}
/* Allocate the arrays to store the input powers associated with each
coefficient, and the coefficient values. Reset the coefficient count
for each axis to zero afterwards so that we can use the array as an index
to the next vacant slot withint he following loop. */
for( i = 0; i < nout; i++ ) {
this->power_f[ i ] = astMalloc( sizeof( int * )*
(size_t) this->ncoeff_f[ i ] );
this->coeff_f[ i ] = astMalloc( sizeof( double )*
(size_t) this->ncoeff_f[ i ] );
this->ncoeff_f[ i ] = 0;
}
if( astOK ) {
/* Extract the coefficient values and powers form the supplied array and
store them in the arrays created above. */
group = coeff;
for( i = 0; i < ncoeff && astOK; i++, group += gsize ) {
iout = floor( group[ 1 ] + 0.5 ) - 1;
ico = ( this->ncoeff_f[ iout ] )++;
this->coeff_f[ iout ][ ico ] = group[ 0 ];
pows = astMalloc( sizeof( int )*(size_t) nin );
this->power_f[ iout ][ ico ] = pows;
if( astOK ) {
for( j = 0; j < nin; j++ ) {
pows[ j ] = floor( group[ 2 + j ] + 0.5 );
}
}
}
}
}
}
/* Now initialise the inverse transformation, if required. */
if( !forward && ncoeff > 0 ) {
/* Create the arrays decribing the inverse transformation. */
this->ncoeff_i = astMalloc( sizeof( int )*(size_t) nin );
this->mxpow_i = astMalloc( sizeof( int )*(size_t) nout );
this->power_i = astMalloc( sizeof( int ** )*(size_t) nin );
this->coeff_i = astMalloc( sizeof( double * )*(size_t) nin );
if( astOK ) {
/* Initialise the count of coefficients for each input coordinate to zero. */
for( i = 0; i < nin; i++ ) this->ncoeff_i[ i ] = 0;
/* Initialise max power for each output coordinate to zero. */
for( j = 0; j < nout; j++ ) this->mxpow_i[ j ] = 0;
/* Scan through the supplied inverse coefficient array, counting the
number of coefficients which relate to each input. Also find the
highest power used for each output axis. Report errors if any unusable
values are found in the supplied array. */
group = coeff;
gsize = 2 + nout;
for( i = 0; i < ncoeff && astOK; i++, group += gsize ) {
iin = floor( group[ 1 ] + 0.5 );
if( iin < 1 || iin > nin ) {
astError( AST__BADCI, "astInitPolyMap(%s): Inverse "
"coefficient %d referred to an illegal input "
"coordinate %d.", status, astGetClass( this ),
i + 1, iin );
astError( AST__BADCI, "This number should be in the "
"range 1 to %d.", status, nin );
break;
}
this->ncoeff_i[ iin - 1 ]++;
for( j = 0; j < nout; j++ ) {
pow = floor( group[ 2 + j ] + 0.5 );
if( pow < 0 ) {
astError( AST__BADPW, "astInitPolyMap(%s): Inverse "
"coefficient %d has a negative power (%d) "
"for output coordinate %d.", status,
astGetClass( this ), i + 1, pow, j + 1 );
astError( AST__BADPW, "All powers should be zero or "
"positive." , status);
break;
}
if( pow > this->mxpow_i[ j ] ) this->mxpow_i[ j ] = pow;
}
}
/* Allocate the arrays to store the output powers associated with each
coefficient, and the coefficient values. Reset the coefficient count
for each axis to zero afterwards so that we can use the array as an index
to the next vacant slot within the following loop. */
for( i = 0; i < nin; i++ ) {
this->power_i[ i ] = astMalloc( sizeof( int * )*
(size_t) this->ncoeff_i[ i ] );
this->coeff_i[ i ] = astMalloc( sizeof( double )*
(size_t) this->ncoeff_i[ i ] );
this->ncoeff_i[ i ] = 0;
}
if( astOK ) {
/* Extract the coefficient values and powers form the supplied array and
store them in the arrays created above. */
group = coeff;
for( i = 0; i < ncoeff && astOK; i++, group += gsize ) {
iin = floor( group[ 1 ] + 0.5 ) - 1;
ico = ( this->ncoeff_i[ iin ] )++;
this->coeff_i[ iin ][ ico ] = group[ 0 ];
pows = astMalloc( sizeof( int )*(size_t) nout );
this->power_i[ iin ][ ico ] = pows;
if( astOK ) {
for( j = 0; j < nout; j++ ) {
pows[ j ] = floor( group[ 2 + j ] + 0.5 );
}
}
}
}
}
}
}
static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* TestAttrib
* Purpose:
* Test if a specified attribute value is set for a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* int TestAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* PolyMap member function (over-rides the astTestAttrib protected
* method inherited from the Mapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* a value has been set for one of a PolyMap's attributes.
* Parameters:
* this
* Pointer to the PolyMap.
* attrib
* Pointer to a null-terminated string specifying the attribute
* name. This should be in lower case with no surrounding white
* space.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if a value has been set, otherwise zero.
* Notes:
* - A value of zero will be returned if this function is invoked
* with the global status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstPolyMap *this; /* Pointer to the PolyMap structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Check the attribute name and test the appropriate attribute. */
/* IterInverse. */
/* ------------ */
if ( !strcmp( attrib, "iterinverse" ) ) {
result = astTestIterInverse( this );
/* NiterInverse. */
/* ------------- */
} else if ( !strcmp( attrib, "niterinverse" ) ) {
result = astTestNiterInverse( this );
/* TolInverse. */
/* ----------- */
} else if ( !strcmp( attrib, "tolinverse" ) ) {
result = astTestTolInverse( this );
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
result = (*parent_testattrib)( this_object, attrib, status );
}
/* Return the result, */
return result;
}
static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a PolyMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* PolyMap member function (over-rides the astTransform protected
* method inherited from the Mapping class).
* Description:
* This function takes a PolyMap and a set of points encapsulated in a
* PointSet and transforms the points.
* Parameters:
* this
* Pointer to the PolyMap.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* Notes:
* - A null pointer will be returned if this function is invoked with the
* global error status set, or if it should fail for any reason.
* - The number of coordinate values per point in the input PointSet must
* match the number of columns in the PolyMap being applied.
* - The number of coordinate values per point in the output PointSet will
* equal the number of rows in the PolyMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstPointSet *result; /* Pointer to output PointSet */
AstPolyMap *map; /* Pointer to PolyMap to be applied */
double **coeff; /* Pointer to coefficient value arrays */
double **ptr_in; /* Pointer to input coordinate data */
double **ptr_out; /* Pointer to output coordinate data */
double **work; /* Pointer to exponentiated axis values */
double *outcof; /* Pointer to next coefficient value */
double *pwork; /* Pointer to exponentiated axis values */
double outval; /* Output axis value */
double term; /* Term to be added to output value */
double x; /* Input axis value */
double xp; /* Exponentiated input axis value */
int ***power; /* Pointer to coefficient power arrays */
int **outpow; /* Pointer to next set of axis powers */
int *mxpow; /* Pointer to max used power for each input */
int *ncoeff; /* Pointer to no. of coefficients */
int in_coord; /* Index of output coordinate */
int ico; /* Coefficient index */
int ip; /* Axis power */
int nc; /* No. of coefficients in polynomial */
int ncoord_in; /* Number of coordinates per input point */
int ncoord_out; /* Number of coordinates per output point */
int npoint; /* Number of points */
int out_coord; /* Index of output coordinate */
int point; /* Loop counter for points */
int pow; /* Next axis power */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the PolyMap. */
map = (AstPolyMap *) this;
/* Apply the parent mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* Determine whether to apply the forward or inverse mapping, according to the
direction specified and whether the mapping has been inverted. */
if ( astGetInvert( map ) ) forward = !forward;
/* We will now extend the parent astTransform method by performing the
calculations needed to generate the output coordinate values. */
/* If we are using the inverse transformatiom, and the IterInverse
attribute is non-zero, use an iterative inverse algorithm rather than any
inverse transformation defined within the PolyMap. */
if( !forward && astGetIterInverse(map) ) {
IterInverse( map, in, result, status );
/* Otherwise, determine the numbers of points and coordinates per point from
the input and output PointSets and obtain pointers for accessing the input
and output coordinate values. */
} else {
ncoord_in = astGetNcoord( in );
ncoord_out = astGetNcoord( result );
npoint = astGetNpoint( in );
ptr_in = astGetPoints( in );
ptr_out = astGetPoints( result );
/* Get a pointer to the arrays holding the required coefficient
values and powers, according to the direction of mapping required. */
if ( forward ) {
ncoeff = map->ncoeff_f;
coeff = map->coeff_f;
power = map->power_f;
mxpow = map->mxpow_f;
} else {
ncoeff = map->ncoeff_i;
coeff = map->coeff_i;
power = map->power_i;
mxpow = map->mxpow_i;
}
/* Allocate memory to hold the required powers of the input axis values. */
work = astMalloc( sizeof( double * )*(size_t) ncoord_in );
for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) {
work[ in_coord ] = astMalloc( sizeof( double )*(size_t) ( mxpow[ in_coord ] + 1 ) );
}
/* Perform coordinate arithmetic. */
/* ------------------------------ */
if ( astOK ) {
/* Loop to apply the polynomial to each point in turn.*/
for ( point = 0; point < npoint; point++ ) {
/* Find the required powers of the input axis values and store them in
the work array. */
for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) {
pwork = work[ in_coord ];
pwork[ 0 ] = 1.0;
x = ptr_in[ in_coord ][ point ];
if( x == AST__BAD ) {
for( ip = 1; ip <= mxpow[ in_coord ]; ip++ ) pwork[ ip ] = AST__BAD;
} else {
for( ip = 1; ip <= mxpow[ in_coord ]; ip++ ) {
pwork[ ip ] = pwork[ ip - 1 ]*x;
}
}
}
/* Loop round each output. */
for( out_coord = 0; out_coord < ncoord_out; out_coord++ ) {
/* Initialise the output value. */
outval = 0.0;
/* Get pointers to the coefficients and powers for this output. */
outcof = coeff[ out_coord ];
outpow = power[ out_coord ];
/* Loop round all polynomial coefficients.*/
nc = ncoeff[ out_coord ];
for ( ico = 0; ico < nc && outval != AST__BAD;
ico++, outcof++, outpow++ ) {
/* Initialise the current term to be equal to the value of the coefficient.
If it is bad, store a bad output value. */
term = *outcof;
if( term == AST__BAD ) {
outval = AST__BAD;
/* Otherwise, loop round all inputs */
} else {
for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) {
/* Get the power of the current input axis value used by the current
coefficient. If it is zero, pass on. */
pow = (*outpow)[ in_coord ];
if( pow > 0 ) {
/* Get the axis value raised to the appropriate power. */
xp = work[ in_coord ][ pow ];
/* If bad, set the output value bad and break. */
if( xp == AST__BAD ) {
outval = AST__BAD;
break;
/* Otherwise multiply the current term by the exponentiated axis value. */
} else {
term *= xp;
}
}
}
}
/* Increment the output value by the current term of the polynomial. */
outval += term;
}
/* Store the output value. */
ptr_out[ out_coord ][ point ] = outval;
}
}
}
/* Free resources. */
for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) {
work[ in_coord ] = astFree( work[ in_coord ] );
}
work = astFree( work );
}
/* Return a pointer to the output PointSet. */
return result;
}
/* Functions which access class attributes. */
/* ---------------------------------------- */
/* Implement member functions to access the attributes associated with
this class using the macros defined for this purpose in the
"object.h" file. For a description of each attribute, see the class
interface (in the associated .h file). */
/*
*att++
* Name:
* IterInverse
* Purpose:
* Provide an iterative inverse transformation?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute indicates whether the inverse transformation of
* the PolyMap should be implemented via an iterative Newton-Raphson
* approximation that uses the forward transformation to transform
* candidate input positions until an output position is found which
* is close to the required output position. By default, an iterative
* inverse is provided if, and only if, no inverse polynomial was supplied
* when the PolyMap was constructed.
*
* The NiterInverse and TolInverse attributes provide parameters that
* control the behaviour of the inverse approcimation method.
* Applicability:
* PolyMap
* All PolyMaps have this attribute.
* Notes:
* - An iterative inverse can only be used if the PolyMap has equal
* numbers of inputs and outputs, as given by the Nin and Nout
* attributes. An error will be reported if IterInverse is set non-zero
* for a PolyMap that does not meet this requirement.
*att--
*/
astMAKE_CLEAR(PolyMap,IterInverse,iterinverse,-INT_MAX)
astMAKE_GET(PolyMap,IterInverse,int,0,( ( this->iterinverse == -INT_MAX ) ?
(this->ncoeff_i == 0) : this->iterinverse ))
astMAKE_SET(PolyMap,IterInverse,int,iterinverse,
(((astGetNin(this)==astGetNout(this))||!value)?((value?1:0)):(astError(AST__ATTIN,"astSetIterInverse(%s):"
"Cannot use an iterative inverse because the %s has unequal numbers of "
"inputs and outputs.", status, astGetClass(this),astGetClass(this)),this->iterinverse)))
astMAKE_TEST(PolyMap,IterInverse,( this->iterinverse != -INT_MAX ))
/* NiterInverse. */
/* --------- */
/*
*att++
* Name:
* NiterInverse
* Purpose:
* Maximum number of iterations for the iterative inverse transformation.
* Type:
* Public attribute.
* Synopsis:
* Integer.
* Description:
* This attribute controls the iterative inverse transformation
* used if the IterInverse attribute is non-zero.
*
* Its value gives the maximum number of iterations of the
* Newton-Raphson algorithm to be used for each transformed position.
* The default value is 4. See also attribute TolInverse.
* Applicability:
* PolyMap
* All PolyMaps have this attribute.
*att--
*/
astMAKE_CLEAR(PolyMap,NiterInverse,niterinverse,-INT_MAX)
astMAKE_GET(PolyMap,NiterInverse,int,0,( this->niterinverse == -INT_MAX ? 4 : this->niterinverse))
astMAKE_SET(PolyMap,NiterInverse,int,niterinverse,value)
astMAKE_TEST(PolyMap,NiterInverse,( this->niterinverse != -INT_MAX ))
/* TolInverse. */
/* ----------- */
/*
*att++
* Name:
* TolInverse
* Purpose:
* Target relative error for the iterative inverse transformation.
* Type:
* Public attribute.
* Synopsis:
* Floating point.
* Description:
* This attribute controls the iterative inverse transformation
* used if the IterInverse attribute is non-zero.
*
* Its value gives the target relative error in teh axis values of
* each transformed position. Further iterations will be performed
* until the target relative error is reached, or the maximum number
* of iterations given by attribute NiterInverse is reached.
* The default value is 1.0E-6.
* Applicability:
* PolyMap
* All PolyMaps have this attribute.
*att--
*/
astMAKE_CLEAR(PolyMap,TolInverse,tolinverse,AST__BAD)
astMAKE_GET(PolyMap,TolInverse,double,0.0,( this->tolinverse == AST__BAD ? 1.0E-6 : this->tolinverse))
astMAKE_SET(PolyMap,TolInverse,double,tolinverse,value)
astMAKE_TEST(PolyMap,TolInverse,( this->tolinverse != AST__BAD ))
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for PolyMap objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for PolyMap objects.
* Parameters:
* objin
* Pointer to the object to be copied.
* objout
* Pointer to the object being constructed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* - This constructor makes a deep copy, including a copy of the
* coefficients associated with the input PolyMap.
*/
/* Local Variables: */
AstPolyMap *in; /* Pointer to input PolyMap */
AstPolyMap *out; /* Pointer to output PolyMap */
int nin; /* No. of input coordinates */
int nout; /* No. of output coordinates */
int i; /* Loop count */
int j; /* Loop count */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output PolyMaps. */
in = (AstPolyMap *) objin;
out = (AstPolyMap *) objout;
/* Nullify the pointers stored in the output object since these will
currently be pointing at the input data (since the output is a simple
byte-for-byte copy of the input). Otherwise, the input data could be
freed by accidient if the output object is deleted due to an error
occuring in this function. */
out->ncoeff_f = NULL;
out->power_f = NULL;
out->coeff_f = NULL;
out->mxpow_f = NULL;
out->ncoeff_i = NULL;
out->power_i = NULL;
out->coeff_i = NULL;
out->mxpow_i = NULL;
out->jacobian = NULL;
/* Get the number of inputs and outputs of the uninverted Mapping. */
nin = ( (AstMapping *) in )->nin;
nout = ( (AstMapping *) in )->nout;
/* Copy the number of coefficients associated with each output of the forward
transformation. */
if( in->ncoeff_f ) {
out->ncoeff_f = (int *) astStore( NULL, (void *) in->ncoeff_f,
sizeof( int )*(size_t) nout );
/* Copy the maximum power of each input axis value used by the forward
transformation. */
out->mxpow_f = (int *) astStore( NULL, (void *) in->mxpow_f,
sizeof( int )*(size_t) nin );
/* Copy the coefficient values used by the forward transformation. */
if( in->coeff_f ) {
out->coeff_f = astMalloc( sizeof( double * )*(size_t) nout );
if( astOK ) {
for( i = 0; i < nout; i++ ) {
out->coeff_f[ i ] = (double *) astStore( NULL, (void *) in->coeff_f[ i ],
sizeof( double )*(size_t) in->ncoeff_f[ i ] );
}
}
}
/* Copy the input axis powers associated with each coefficient of the forward
transformation. */
if( in->power_f ) {
out->power_f = astMalloc( sizeof( int ** )*(size_t) nout );
if( astOK ) {
for( i = 0; i < nout; i++ ) {
out->power_f[ i ] = astMalloc( sizeof( int * )*(size_t) in->ncoeff_f[ i ] );
if( astOK ) {
for( j = 0; j < in->ncoeff_f[ i ]; j++ ) {
out->power_f[ i ][ j ] = (int *) astStore( NULL, (void *) in->power_f[ i ][ j ],
sizeof( int )*(size_t) nin );
}
}
}
}
}
}
/* Do the same for the inverse transformation. */
if( in->ncoeff_i ) {
out->ncoeff_i = (int *) astStore( NULL, (void *) in->ncoeff_i,
sizeof( int )*(size_t) nin );
out->mxpow_i = (int *) astStore( NULL, (void *) in->mxpow_i,
sizeof( int )*(size_t) nout );
if( in->coeff_i ) {
out->coeff_i = astMalloc( sizeof( double * )*(size_t) nin );
if( astOK ) {
for( i = 0; i < nin; i++ ) {
out->coeff_i[ i ] = (double *) astStore( NULL, (void *) in->coeff_i[ i ],
sizeof( double )*(size_t) in->ncoeff_i[ i ] );
}
}
}
if( in->power_i ) {
out->power_i = astMalloc( sizeof( int ** )*(size_t) nin );
if( astOK ) {
for( i = 0; i < nin; i++ ) {
out->power_i[ i ] = astMalloc( sizeof( int * )*(size_t) in->ncoeff_i[ i ] );
if( astOK ) {
for( j = 0; j < in->ncoeff_i[ i ]; j++ ) {
out->power_i[ i ][ j ] = (int *) astStore( NULL, (void *) in->power_i[ i ][ j ],
sizeof( int )*(size_t) nout );
}
}
}
}
}
}
/* If an error has occurred, free al the resources allocated above. */
if( !astOK ) {
FreeArrays( out, 1, status );
FreeArrays( out, 0, status );
}
return;
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for PolyMap objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for PolyMap objects.
* Parameters:
* obj
* Pointer to the object to be deleted.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
AstPolyMap *this;
int nc;
int ic;
int lstat;
int error;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) obj;
/* Free the arrays. */
FreeArrays( this, 1, status );
FreeArrays( this, 0, status );
/* Free the resources used to store the Jacobian of the forward
transformation. */
if( this->jacobian ) {
/* Get the number of PolyMap inputs. We need to clear any error status
first since astGetNin returns zero if an error has occurred. The
Jacobian will only be non-NULL if the number of inputs and outputs
are equal. */
error = !astOK;
if( error ) {
lstat = astStatus;
astClearStatus;
}
nc = astGetNin( this );
if( error ) astSetStatus( lstat );
for( ic = 0; ic < nc; ic++ ) {
(this->jacobian)[ ic ] = astAnnul( (this->jacobian)[ ic ] );
}
this->jacobian = astFree( this->jacobian );
}
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for PolyMap objects.
* Type:
* Private function.
* Synopsis:
* void Dump( AstObject *this, AstChannel *channel, int *status )
* Description:
* This function implements the Dump function which writes out data
* for the PolyMap class to an output Channel.
* Parameters:
* this
* Pointer to the PolyMap whose data are being written.
* channel
* Pointer to the Channel to which the data are being written.
* status
* Pointer to the inherited status variable.
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstPolyMap *this; /* Pointer to the PolyMap structure */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
char comm[ 100 ]; /* Buffer for comment string */
double dval; /* Floating point attribute value */
int i; /* Loop index */
int iv; /* Vectorised keyword index */
int ival; /* Integer value */
int j; /* Loop index */
int k; /* Loop index */
int nin; /* No. of input coords */
int nout; /* No. of output coords */
int set; /* Attribute value set? */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the PolyMap structure. */
this = (AstPolyMap *) this_object;
/* Find the number of inputs and outputs of the uninverted Mapping. */
nin = ( (AstMapping *) this )->nin;
nout = ( (AstMapping *) this )->nout;
/* Write out values representing the instance variables for the
PolyMap class. */
/* First do the forward transformation arrays. Check they are used. */
if( this->ncoeff_f ) {
/* Store the maximum power of each input axis value used by the forward
transformation. */
for( i = 0; i < nin; i++ ){
(void) sprintf( buff, "MPF%d", i + 1 );
(void) sprintf( comm, "Max. power of input %d in any forward polynomial", i + 1 );
astWriteInt( channel, buff, 1, 1, (this->mxpow_f)[ i ], comm );
}
/* Store the number of coefficients associated with each output of the forward
transformation. */
for( i = 0; i < nout; i++ ){
(void) sprintf( buff, "NCF%d", i + 1 );
(void) sprintf( comm, "No. of coeff.s for forward polynomial %d", i + 1 );
astWriteInt( channel, buff, 1, 1, (this->ncoeff_f)[ i ], comm );
}
/* Store the coefficient values used by the forward transformation. */
iv = 1;
for( i = 0; i < nout; i++ ){
for( j = 0; j < this->ncoeff_f[ i ]; j++, iv++ ){
if( (this->coeff_f)[ i ][ j ] != AST__BAD ) {
(void) sprintf( buff, "CF%d", iv );
(void) sprintf( comm, "Coeff %d of forward polynomial %d", j + 1, i + 1 );
astWriteDouble( channel, buff, 1, 1, (this->coeff_f)[ i ][ j ], comm );
}
}
}
/* Store the input axis powers associated with each coefficient of the forward
transformation. */
iv = 1;
for( i = 0; i < nout; i++ ){
for( j = 0; j < this->ncoeff_f[ i ]; j++ ){
for( k = 0; k < nin; k++, iv++ ){
if( (this->power_f)[ i ][ j ][ k ] > 0 ) {
(void) sprintf( buff, "PF%d", iv );
(void) sprintf( comm, "Power of i/p %d for coeff %d of fwd poly %d", k + 1, j + 1, i + 1 );
astWriteDouble( channel, buff, 1, 1, (this->power_f)[ i ][ j ][ k ], comm );
}
}
}
}
}
/* Now do the inverse transformation arrays. Check they are used. */
if( this->ncoeff_i ) {
/* Store the maximum power of each output axis value used by the inverse
transformation. */
for( i = 0; i < nout; i++ ){
(void) sprintf( buff, "MPI%d", i + 1 );
(void) sprintf( comm, "Max. power of output %d in any inverse polynomial", i + 1 );
astWriteInt( channel, buff, 1, 1, (this->mxpow_i)[ i ], comm );
}
/* Store the number of coefficients associated with each input of the inverse
transformation. */
for( i = 0; i < nin; i++ ){
(void) sprintf( buff, "NCI%d", i + 1 );
(void) sprintf( comm, "No. of coeff.s for inverse polynomial %d", i + 1 );
astWriteInt( channel, buff, 1, 1, (this->ncoeff_i)[ i ], comm );
}
/* Store the coefficient values used by the inverse transformation. */
iv = 1;
for( i = 0; i < nin; i++ ){
for( j = 0; j < this->ncoeff_i[ i ]; j++, iv++ ){
if( (this->coeff_i)[ i ][ j ] != AST__BAD ) {
(void) sprintf( buff, "CI%d", iv );
(void) sprintf( comm, "Coeff %d of inverse polynomial %d", j + 1, i + 1 );
astWriteDouble( channel, buff, 1, 1, (this->coeff_i)[ i ][ j ], comm );
}
}
}
/* Store the output axis powers associated with each coefficient of the inverse
transformation. */
iv = 1;
for( i = 0; i < nin; i++ ){
for( j = 0; j < this->ncoeff_i[ i ]; j++ ){
for( k = 0; k < nout; k++, iv++ ){
if( (this->power_i)[ i ][ j ][ k ] > 0 ) {
(void) sprintf( buff, "PI%d", iv );
(void) sprintf( comm, "Power of o/p %d for coeff %d of inv poly %d", k + 1, j + 1, i + 1 );
astWriteDouble( channel, buff, 1, 1, (this->power_i)[ i ][ j ][ k ], comm );
}
}
}
}
}
/* Use an iterative inverse? */
set = TestIterInverse( this, status );
ival = set ? GetIterInverse( this, status ) : astGetIterInverse( this );
astWriteInt( channel, "IterInv", set, 0, ival, ival ? "Use an iterative inverse" : "Do not use an iterative inverse" );
/* Max number of iterations for iterative inverse. */
set = TestNiterInverse( this, status );
ival = set ? GetNiterInverse( this, status ) : astGetNiterInverse( this );
astWriteInt( channel, "NiterInv", set, 0, ival, "Max number of iterations for iterative inverse" );
/* Target relative error for iterative inverse. */
set = TestTolInverse( this, status );
dval = set ? GetTolInverse( this, status ) : astGetTolInverse( this );
astWriteDouble( channel, "TolInv", set, 0, dval, "Target relative error for iterative inverse" );
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAPolyMap and astCheckPolyMap functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(PolyMap,Mapping)
astMAKE_CHECK(PolyMap)
AstPolyMap *astPolyMap_( int nin, int nout, int ncoeff_f, const double coeff_f[],
int ncoeff_i, const double coeff_i[], const char *options, int *status, ...){
/*
*++
* Name:
c astPolyMap
f AST_POLYMAP
* Purpose:
* Create a PolyMap.
* Type:
* Public function.
* Synopsis:
c #include "polymap.h"
c AstPolyMap *astPolyMap( int nin, int nout, int ncoeff_f, const double coeff_f[],
c int ncoeff_i, const double coeff_i[],
c const char *options, ... )
f RESULT = AST_POLYMAP( NIN, NOUT, NCOEFF_F, COEFF_F, NCOEFF_I, COEFF_I,
f OPTIONS, STATUS )
* Class Membership:
* PolyMap constructor.
* Description:
* This function creates a new PolyMap and optionally initialises
* its attributes.
*
* A PolyMap is a form of Mapping which performs a general polynomial
* transformation. Each output coordinate is a polynomial function of
* all the input coordinates. The coefficients are specified separately
* for each output coordinate. The forward and inverse transformations
* are defined independantly by separate sets of coefficients. If no
* inverse transformation is supplied, an iterative method can be used
* to evaluate the inverse based only on the forward transformation.
* Parameters:
c nin
f NIN = INTEGER (Given)
* The number of input coordinates.
c nout
f NOUT = INTEGER (Given)
* The number of output coordinates.
c ncoeff_f
f NCOEFF_F = INTEGER (Given)
* The number of non-zero coefficients necessary to define the
* forward transformation of the PolyMap. If zero is supplied, the
* forward transformation will be undefined.
c coeff_f
f COEFF_F( * ) = DOUBLE PRECISION (Given)
* An array containing
c "ncoeff_f*( 2 + nin )" elements. Each group of "2 + nin"
f "NCOEFF_F*( 2 + NIN )" elements. Each group of "2 + NIN"
* adjacent elements describe a single coefficient of the forward
* transformation. Within each such group, the first element is the
* coefficient value; the next element is the integer index of the
* PolyMap output which uses the coefficient within its defining
* polynomial (the first output has index 1); the remaining elements
* of the group give the integer powers to use with each input
* coordinate value (powers must not be negative, and floating
* point values are rounded to the nearest integer).
c If "ncoeff_f" is zero, a NULL pointer may be supplied for "coeff_f".
*
* For instance, if the PolyMap has 3 inputs and 2 outputs, each group
* consisting of 5 elements, A groups such as "(1.2, 2.0, 1.0, 3.0, 0.0)"
* describes a coefficient with value 1.2 which is used within the
* definition of output 2. The output value is incremented by the
* product of the coefficient value, the value of input coordinate
* 1 raised to the power 1, and the value of input coordinate 2 raised
* to the power 3. Input coordinate 3 is not used since its power is
* specified as zero. As another example, the group "(-1.0, 1.0,
* 0.0, 0.0, 0.0 )" describes adds a constant value -1.0 onto
* output 1 (it is a constant value since the power for every input
* axis is given as zero).
*
c Each final output coordinate value is the sum of the "ncoeff_f" terms
c described by the "ncoeff_f" groups within the supplied array.
f Each final output coordinate value is the sum of the "NCOEFF_F" terms
f described by the "NCOEFF_F" groups within the supplied array.
c ncoeff_i
f NCOEFF_I = INTEGER (Given)
* The number of non-zero coefficients necessary to define the
* inverse transformation of the PolyMap. If zero is supplied, the
* inverse transformation will be undefined.
c coeff_i
f COEFF_I( * ) = DOUBLE PRECISION (Given)
* An array containing
c "ncoeff_i*( 2 + nout )" elements. Each group of "2 + nout"
f "NCOEFF_I*( 2 + NOUT )" elements. Each group of "2 + NOUT"
* adjacent elements describe a single coefficient of the inverse
c transformation, using the same schame as "coeff_f",
f transformation, using the same schame as "COEFF_F",
* except that "inputs" and "outputs" are transposed.
c If "ncoeff_i" is zero, a NULL pointer may be supplied for "coeff_i".
c options
f OPTIONS = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated string containing an optional
c comma-separated list of attribute assignments to be used for
c initialising the new PolyMap. The syntax used is identical to
c that for the astSet function and may include "printf" format
c specifiers identified by "%" symbols in the normal way.
f A character string containing an optional comma-separated
f list of attribute assignments to be used for initialising the
f new PolyMap. The syntax used is identical to that for the
f AST_SET routine.
c ...
c If the "options" string contains "%" format specifiers, then
c an optional list of additional arguments may follow it in
c order to supply values to be substituted for these
c specifiers. The rules for supplying these are identical to
c those for the astSet function (and for the C "printf"
c function).
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astPolyMap()
f AST_POLYMAP = INTEGER
* A pointer to the new PolyMap.
* Notes:
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstPolyMap *new; /* Pointer to new PolyMap */
va_list args; /* Variable argument list */
/* Check the global status. */
if ( !astOK ) return NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialise the PolyMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitPolyMap( NULL, sizeof( AstPolyMap ), !class_init,
&class_vtab, "PolyMap", nin, nout,
ncoeff_f, coeff_f, ncoeff_i, coeff_i );
/* If successful, note that the virtual function table has been
initialised. */
if ( astOK ) {
class_init = 1;
/* Obtain the variable argument list and pass it along with the options string
to the astVSet method to initialise the new PolyMap's attributes. */
va_start( args, status );
astVSet( new, options, NULL, args );
va_end( args );
/* If an error occurred, clean up by deleting the new object. */
if ( !astOK ) new = astDelete( new );
}
/* Return a pointer to the new PolyMap. */
return new;
}
AstPolyMap *astPolyMapId_( int nin, int nout, int ncoeff_f, const double coeff_f[],
int ncoeff_i, const double coeff_i[], const char *options, ... ){
/*
* Name:
* astPolyMapId_
* Purpose:
* Create a PolyMap.
* Type:
* Private function.
* Synopsis:
* #include "polymap.h"
* AstPolyMap *astPolyMap( int nin, int nout, int ncoeff_f, const double coeff_f[],
* int ncoeff_i, const double coeff_i[],
* const char *options, ... )
* Class Membership:
* PolyMap constructor.
* Description:
* This function implements the external (public) interface to the
* astPolyMap constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astPolyMap_ has a variable argument list which cannot be
* encapsulated in a macro (where this conversion would otherwise
* occur).
*
* The variable argument list also prevents this function from
* invoking astPolyMap_ directly, so it must be a re-implementation
* of it in all respects, except for the final conversion of the
* result to an ID value.
* Parameters:
* As for astPolyMap_.
* Returned Value:
* The ID value associated with the new PolyMap.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstPolyMap *new; /* Pointer to new PolyMap */
va_list args; /* Variable argument list */
int *status; /* Pointer to inherited status value */
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Check the global status. */
if ( !astOK ) return NULL;
/* Initialise the PolyMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitPolyMap( NULL, sizeof( AstPolyMap ), !class_init,
&class_vtab, "PolyMap", nin, nout,
ncoeff_f, coeff_f, ncoeff_i, coeff_i );
/* If successful, note that the virtual function table has been
initialised. */
if ( astOK ) {
class_init = 1;
/* Obtain the variable argument list and pass it along with the options string
to the astVSet method to initialise the new PolyMap's attributes. */
va_start( args, options );
astVSet( new, options, NULL, args );
va_end( args );
/* If an error occurred, clean up by deleting the new object. */
if ( !astOK ) new = astDelete( new );
}
/* Return an ID value for the new PolyMap. */
return astMakeId( new );
}
AstPolyMap *astInitPolyMap_( void *mem, size_t size, int init,
AstPolyMapVtab *vtab, const char *name,
int nin, int nout, int ncoeff_f, const double coeff_f[],
int ncoeff_i, const double coeff_i[], int *status ){
/*
*+
* Name:
* astInitPolyMap
* Purpose:
* Initialise a PolyMap.
* Type:
* Protected function.
* Synopsis:
* #include "polymap.h"
* AstPolyMap *astInitPolyMap( void *mem, size_t size, int init,
* AstPolyMapVtab *vtab, const char *name,
* int nin, int nout, int ncoeff_f,
* const double coeff_f[], int ncoeff_i,
* const double coeff_i[] )
* Class Membership:
* PolyMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new PolyMap object. It allocates memory (if necessary) to accommodate
* the PolyMap plus any additional data associated with the derived class.
* It then initialises a PolyMap structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a PolyMap at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the PolyMap is to be initialised.
* This must be of sufficient size to accommodate the PolyMap data
* (sizeof(PolyMap)) plus any data used by the derived class. If a value
* of NULL is given, this function will allocate the memory itself using
* the "size" parameter to determine its size.
* size
* The amount of memory used by the PolyMap (plus derived class data).
* This will be used to allocate memory if a value of NULL is given for
* the "mem" parameter. This value is also stored in the PolyMap
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the PolyMap's virtual function table is
* to be initialised. If this value is non-zero, the virtual function
* table will be initialised by this function.
* vtab
* Pointer to the start of the virtual function table to be associated
* with the new PolyMap.
* name
* Pointer to a constant null-terminated character string which contains
* the name of the class to which the new object belongs (it is this
* pointer value that will subsequently be returned by the astGetClass
* method).
* nin
* The number of input coordinate values per point. This is the
* same as the number of columns in the matrix.
* nout
* The number of output coordinate values per point. This is the
* same as the number of rows in the matrix.
* ncoeff_f
* The number of non-zero coefficients necessary to define the
* forward transformation of the PolyMap. If zero is supplied, the
* forward transformation will be undefined.
* coeff_f
* An array containing "ncoeff_f*( 2 + nin )" elements. Each group
* of "2 + nin" adjacent elements describe a single coefficient of
* the forward transformation. Within each such group, the first
* element is the coefficient value; the next element is the
* integer index of the PolyMap output which uses the coefficient
* within its defining polynomial (the first output has index 1);
* the remaining elements of the group give the integer powers to
* use with each input coordinate value (powers must not be
* negative)
*
* For instance, if the PolyMap has 3 inputs and 2 outputs, each group
* consisting of 5 elements, A groups such as "(1.2, 2.0, 1.0, 3.0, 0.0)"
* describes a coefficient with value 1.2 which is used within the
* definition of output 2. The output value is incremented by the
* product of the coefficient value, the value of input coordinate
* 1 raised to the power 1, and the value of input coordinate 2 raised
* to the power 3. Input coordinate 3 is not used since its power is
* specified as zero. As another example, the group "(-1.0, 1.0,
* 0.0, 0.0, 0.0 )" describes adds a constant value -1.0 onto
* output 1 (it is a constant value since the power for every input
* axis is given as zero).
*
* Each final output coordinate value is the sum of the "ncoeff_f" terms
* described by the "ncoeff_f" groups within the supplied array.
* ncoeff_i
* The number of non-zero coefficients necessary to define the
* inverse transformation of the PolyMap. If zero is supplied, the
* inverse transformation will be undefined.
* coeff_i
* An array containing
* "ncoeff_i*( 2 + nout )" elements. Each group of "2 + nout"
* adjacent elements describe a single coefficient of the inverse
* transformation, using the same schame as "coeff_f", except that
* "inputs" and "outputs" are transposed.
* Returned Value:
* A pointer to the new PolyMap.
* Notes:
* - A null pointer will be returned if this function is invoked with the
* global error status set, or if it should fail for any reason.
*-
*/
/* Local Variables: */
AstPolyMap *new; /* Pointer to new PolyMap */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitPolyMapVtab( vtab, name );
/* Initialise a Mapping structure (the parent class) as the first component
within the PolyMap structure, allocating memory if necessary. Specify that
the Mapping should be defined in both the forward and inverse directions. */
new = (AstPolyMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
nin, nout, 1, 1 );
if ( astOK ) {
/* Initialise the PolyMap data. */
/* ---------------------------- */
/* First initialise the pointers in case of errors. */
new->ncoeff_f = NULL;
new->power_f = NULL;
new->coeff_f = NULL;
new->mxpow_f = NULL;
new->ncoeff_i = NULL;
new->power_i = NULL;
new->coeff_i = NULL;
new->mxpow_i = NULL;
/* Store the forward transformation. */
StoreArrays( new, 1, ncoeff_f, coeff_f, status );
/* Store the inverse transformation. */
StoreArrays( new, 0, ncoeff_i, coeff_i, status );
/* Other class attributes. */
new->iterinverse = -INT_MAX;
new->niterinverse = -INT_MAX;
new->tolinverse = AST__BAD;
new->jacobian = NULL;
/* If an error occurred, clean up by deleting the new PolyMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return a pointer to the new PolyMap. */
return new;
}
AstPolyMap *astLoadPolyMap_( void *mem, size_t size,
AstPolyMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadPolyMap
* Purpose:
* Load a PolyMap.
* Type:
* Protected function.
* Synopsis:
* #include "polymap.h"
* AstPolyMap *astLoadPolyMap( void *mem, size_t size,
* AstPolyMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* PolyMap loader.
* Description:
* This function is provided to load a new PolyMap using data read
* from a Channel. It first loads the data used by the parent class
* (which allocates memory if necessary) and then initialises a
* PolyMap structure in this memory, using data read from the input
* Channel.
*
* If the "init" flag is set, it also initialises the contents of a
* virtual function table for a PolyMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the PolyMap is to be
* loaded. This must be of sufficient size to accommodate the
* PolyMap data (sizeof(PolyMap)) plus any data used by derived
* classes. If a value of NULL is given, this function will
* allocate the memory itself using the "size" parameter to
* determine its size.
* size
* The amount of memory used by the PolyMap (plus derived class
* data). This will be used to allocate memory if a value of
* NULL is given for the "mem" parameter. This value is also
* stored in the PolyMap structure, so a valid value must be
* supplied even if not required for allocating memory.
*
* If the "vtab" parameter is NULL, the "size" value is ignored
* and sizeof(AstPolyMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new PolyMap. If this is NULL, a pointer
* to the (static) virtual function table for the PolyMap class
* is used instead.
* name
* Pointer to a constant null-terminated character string which
* contains the name of the class to which the new object
* belongs (it is this pointer value that will subsequently be
* returned by the astGetClass method).
*
* If the "vtab" parameter is NULL, the "name" value is ignored
* and a pointer to the string "PolyMap" is used instead.
* Returned Value:
* A pointer to the new PolyMap.
* Notes:
* - A null pointer will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*-
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
/* Local Variables: */
AstPolyMap *new; /* Pointer to the new PolyMap */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
int i; /* Loop index */
int iv; /* Vectorised keyword index */
int j; /* Loop index */
int k; /* Loop index */
int nin; /* No. of input coords */
int nout; /* No. of output coords */
int undef; /* Is the transformation undefined? */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(channel);
/* Initialise. */
new = NULL;
/* Check the global error status. */
if ( !astOK ) return new;
/* If a NULL virtual function table has been supplied, then this is
the first loader to be invoked for this PolyMap. In this case the
PolyMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstPolyMap );
vtab = &class_vtab;
name = "PolyMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitPolyMapVtab( vtab, name );
class_init = 1;
}
}
/* Invoke the parent class loader to load data for all the ancestral
classes of the current one, returning a pointer to the resulting
partly-built PolyMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name,
channel );
if ( astOK ) {
/* Get the number of inputs and outputs for the uninverted Mapping. */
nin = ( (AstMapping *) new )->nin;
nout = ( (AstMapping *) new )->nout;
/* Read input data. */
/* ================ */
/* Request the input Channel to read all the input data appropriate to
this class into the internal "values list". */
astReadClassData( channel, "PolyMap" );
/* Allocate memory to hold the forward arrays. */
new->ncoeff_f = astMalloc( sizeof( int )*(size_t) nout );
new->mxpow_f = astMalloc( sizeof( int )*(size_t) nin );
new->power_f = astMalloc( sizeof( int ** )*(size_t) nout );
new->coeff_f = astMalloc( sizeof( double * )*(size_t) nout );
if( astOK ) {
/* Assume the forward transformation is defined. */
undef = 0;
/* Get the maximum power of each input axis value used by the forward
transformation. Set a flag "undef" if no values relating to the
forward transformation are found (this indicates that the forward
transformation is not defined). */
for( i = 0; i < nin && !undef; i++ ){
(void) sprintf( buff, "mpf%d", i + 1 );
(new->mxpow_f)[ i ] = astReadInt( channel, buff, INT_MAX );
if( (new->mxpow_f)[ i ] == INT_MAX ) undef = 1;
}
/* Get the number of coefficients associated with each output of the forward
transformation. */
for( i = 0; i < nout && !undef; i++ ){
(void) sprintf( buff, "ncf%d", i + 1 );
(new->ncoeff_f)[ i ] = astReadInt( channel, buff, INT_MAX );
if( (new->ncoeff_f)[ i ] == INT_MAX ) undef = 1;
}
/* Get the coefficient values used by the forward transformation. This
uses new style vectorised key names if available. Otherwise it uses
old style indexed names (which were superceded by vectorised names
because they are shorter and so work better with FitsChans). */
iv = 0;
for( i = 0; i < nout && !undef; i++ ){
(new->coeff_f)[ i ] = astMalloc( sizeof( double )*
(size_t) new->ncoeff_f[ i ] );
if( astOK ) {
for( j = 0; j < new->ncoeff_f[ i ]; j++ ){
(void) sprintf( buff, "cf%d", ++iv );
(new->coeff_f)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD );
if( (new->coeff_f)[ i ][ j ] == AST__BAD ) {
(void) sprintf( buff, "cf%d_%d", i + 1, j + 1 );
(new->coeff_f)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD );
}
}
}
}
/* Get the input axis powers associated with each coefficient of the forward
transformation. */
iv = 0;
for( i = 0; i < nout && !undef; i++ ){
(new->power_f)[ i ] = astMalloc( sizeof( int * )*
(size_t) new->ncoeff_f[ i ] );
if( astOK ) {
for( j = 0; j < new->ncoeff_f[ i ]; j++ ){
(new->power_f)[ i ][ j ] = astMalloc( sizeof( int )* (size_t) nin );
if( astOK ) {
for( k = 0; k < nin; k++ ){
(void) sprintf( buff, "pf%d", ++iv );
(new->power_f)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 );
if( (new->power_f)[ i ][ j ][ k ] == 0 ) {
(void) sprintf( buff, "pf%d_%d_%d", i + 1, j + 1, k + 1 );
(new->power_f)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 );
}
}
}
}
}
}
/* Free the arrays if the forward transformation is undefined. */
if( undef ) {
new->ncoeff_f = astFree( new->ncoeff_f );
new->mxpow_f = astFree( new->mxpow_f );
new->power_f = astFree( new->power_f );
new->coeff_f = astFree( new->coeff_f );
}
}
/* Allocate memory to hold the inverse arrays. */
new->ncoeff_i = astMalloc( sizeof( int )*(size_t) nin );
new->mxpow_i = astMalloc( sizeof( int )*(size_t) nout );
new->power_i = astMalloc( sizeof( int ** )*(size_t) nin );
new->coeff_i = astMalloc( sizeof( double * )*(size_t) nin );
if( astOK ) {
/* Assume the inverse transformation is defined. */
undef = 0;
/* Get the maximum power of each output axis value used by the inverse
transformation. Set a flag "undef" if no values relating to the
inverse transformation are found (this indicates that the inverse
transformation is not defined). */
for( i = 0; i < nout && !undef; i++ ){
(void) sprintf( buff, "mpi%d", i + 1 );
(new->mxpow_i)[ i ] = astReadInt( channel, buff, INT_MAX );
if( (new->mxpow_i)[ i ] == INT_MAX ) undef = 1;
}
/* Get the number of coefficients associated with each input of the inverse
transformation. */
for( i = 0; i < nin && !undef; i++ ){
(void) sprintf( buff, "nci%d", i + 1 );
(new->ncoeff_i)[ i ] = astReadInt( channel, buff, INT_MAX );
if( (new->ncoeff_i)[ i ] == INT_MAX ) undef = 1;
}
/* Get the coefficient values used by the inverse transformation. */
iv = 0;
for( i = 0; i < nin && !undef; i++ ){
(new->coeff_i)[ i ] = astMalloc( sizeof( double )*
(size_t) new->ncoeff_i[ i ] );
if( astOK ) {
for( j = 0; j < new->ncoeff_i[ i ]; j++ ){
(void) sprintf( buff, "ci%d", ++iv );
(new->coeff_i)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD );
if( (new->coeff_i)[ i ][ j ] == AST__BAD ) {
(void) sprintf( buff, "ci%d_%d", i + 1, j + 1 );
(new->coeff_i)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD );
}
}
}
}
/* Get the output axis powers associated with each coefficient of the inverse
transformation. */
iv = 0;
for( i = 0; i < nin && !undef; i++ ){
(new->power_i)[ i ] = astMalloc( sizeof( int * )*
(size_t) new->ncoeff_i[ i ] );
if( astOK ) {
for( j = 0; j < new->ncoeff_i[ i ]; j++ ){
(new->power_i)[ i ][ j ] = astMalloc( sizeof( int )* (size_t) nout );
if( astOK ) {
for( k = 0; k < nout; k++ ){
(void) sprintf( buff, "pi%d", ++iv );
(new->power_i)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 );
if( (new->power_i)[ i ][ j ][ k ] == 0 ) {
(void) sprintf( buff, "pi%d_%d_%d", i + 1, j + 1, k + 1 );
(new->power_i)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 );
}
}
}
}
}
}
/* Free the arrays if the inverse transformation is undefined. */
if( undef ) {
new->ncoeff_i = astFree( new->ncoeff_i );
new->mxpow_i = astFree( new->mxpow_i );
new->power_i = astFree( new->power_i );
new->coeff_i = astFree( new->coeff_i );
}
}
/* Whether to use an iterative inverse transformation. */
new->iterinverse = astReadInt( channel, "iterinv", -INT_MAX );
if ( TestIterInverse( new, status ) ) SetIterInverse( new, new->iterinverse, status );
/* Max number of iterations for iterative inverse transformation. */
new->niterinverse = astReadInt( channel, "niterinv", -INT_MAX );
if ( TestNiterInverse( new, status ) ) SetNiterInverse( new, new->niterinverse, status );
/* Target relative error for iterative inverse transformation. */
new->tolinverse = astReadDouble( channel, "tolinv", AST__BAD );
if ( TestTolInverse( new, status ) ) SetTolInverse( new, new->tolinverse, status );
/* The Jacobian of the PolyMap's forward transformation has not yet been
found. */
new->jacobian = NULL;
/* If an error occurred, clean up by deleting the new PolyMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new PolyMap pointer. */
return new;
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* Virtual function interfaces. */
/* ============================ */
/* These provide the external interface to the virtual functions defined by
this class. Each simply checks the global error status and then locates and
executes the appropriate member function, using the function pointer stored
in the object's virtual function table (this pointer is located using the
astMEMBER macro defined in "object.h").
Note that the member function may not be the one defined here, as it may
have been over-ridden by a derived class. However, it should still have the
same interface. */
AstPolyMap *astPolyTran_( AstPolyMap *this, int forward, double acc,
double maxacc, int maxorder, const double *lbnd,
const double *ubnd, int *status ){
if ( !astOK ) return NULL;
return (**astMEMBER(this,PolyMap,PolyTran))( this, forward, acc,
maxacc, maxorder, lbnd,
ubnd, status );
}
|