#!/usr/local/bin/ruby

# Generate program to create iatan2 function
# Satofumi KAMIMURA
# $Id: create_iatan2.rb 1215 2009-08-07 16:57:26Z satofumi $

# 絶対値の最大値、角度分割数の設定
angle_div = 65536		# [0, 65535]
angle_div8 = angle_div / 8


# テーブル部分の出力

# get this file name
__FILE__ =~ /\/*(.+)$/
thisFile = $1

print <<-EOB
/*
  \\brief 整数型の atan2()

  Integer Arc Tangent program iatan2()
  Attention.  This program generated by #{thisFile}.

  \\author Satofumi KAMIMURA

  $Id: create_iatan2.rb 1215 2009-08-07 16:57:26Z satofumi $

  angle_max = #{angle_div-1}

  現在の実装では、33/65535 以内の誤差が生じる。
  また、利用できる X, Y の範囲は 2^(31-13) まで
*/

#include "iatan2.h"

static short tan_table[] = {
EOB

# テーブルの出力
table_div8 = 256		# テーブルの値の個数
table_div = table_div8 * 8
error_val = 65536 / 8 / table_div8

print "  "
for i in 0 .. table_div8
  t = 2.0 * Math::PI * i.to_f / table_div.to_f
  v = angle_div8.to_f * (Math::sin(t) / Math::cos(t))
  print "#{v.round},"
  if (i+1) % 10 == 0
    print "\n  "
  else
    print " "
  end
end
print "#{(v+1).to_i}\n"		# 番兵

print <<-EOB
};


static int atanValue(int y, int x, int signed_offset) {
  short *p = tan_table;
  short v;
  int ret;
  int offset;

  short *first, *last, *pre;

  offset = (signed_offset < 0) ? -signed_offset : signed_offset ;

  if (x == 0) {
    ;			/* 探索は必要ない、先頭の値をそのまま返す */
  } else {
    v = y * #{angle_div8} / x;
    v = (v < 0) ? -v : v;
    first = tan_table;
    last = tan_table + #{table_div8};

    do {
      pre = p;
      p = tan_table + ((first-tan_table) + (last-tan_table)) / 2;
      if (*p > v) {
        last = p;
      } else {
        first = p;
      }
    } while (p != pre);
  }
  ret = (int)((p - tan_table) * #{error_val});

  if (signed_offset < 0) {
    ret = offset - ret;
  } else {
    ret += offset;
  }
  return ret;
}

#define DEG(X) (int)(1.0 * #{angle_div} * X / 360.0)

/*!
  \\brief 整数型のatan2()

  整数型の atan2()。

  \\Param y [i] Y値
  \\param x [i] X値

  \\retval 角度[div16]
*/
int iatan2(int y, int x) {
  int ret;

  if (y >= 0) {
    if (x >= 0) {	/* 第１象限 */
      if (x >= y) {	/* 0-45[deg] */
        ret = atanValue(y, x, DEG(0));
      } else {		/* 45-90[deg] */
        ret = atanValue(x, y, DEG(-90));
      }
    } else {		/* 第２象限 */
      if (-x <= y) {	/* 90-135[deg] */
        ret = atanValue(x, y, DEG(90));
      } else {		/* 135-180[deg] */
        ret = atanValue(y, x, DEG(-180));
      }
    }
  } else {
    if (x < 0) {	/* 第３象限 */
      if (-x >= -y) {	/* 180-225[deg] */
        ret = atanValue(y, x, DEG(180));
      } else {		/* 225-270[deg] */
        ret = atanValue(x, y, DEG(-270));
      }
    } else {		/* 第４象限 */
      if (x <= -y) {	/* 270-315[deg] */
        ret = atanValue(x, y, DEG(270));
      } else {		/* 315-360[deg] */
        ret = atanValue(y, x, -DEG(360));
      }
    }
  }
  return ret;
}
EOB
