/*
 * Copyright (c) 2006-2009 OrangeSignal.com All rights reserved.
 */

package jp.sourceforge.orangesignal.ta;

import static jp.sourceforge.orangesignal.ta.ArrayDataUtils.getMinLength;
import static jp.sourceforge.orangesignal.ta.ArrayDataUtils.indexOfNotNull;
import static jp.sourceforge.orangesignal.ta.ArrayDataUtils.lastIndexOfNotNull;

import java.util.ArrayList;
import java.util.Date;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

import jp.sourceforge.orangesignal.ta.result.Aroon;
import jp.sourceforge.orangesignal.ta.result.Bands2;
import jp.sourceforge.orangesignal.ta.result.Bands5;
import jp.sourceforge.orangesignal.ta.result.Bands7;
import jp.sourceforge.orangesignal.ta.result.Changes;
import jp.sourceforge.orangesignal.ta.result.DMI;
import jp.sourceforge.orangesignal.ta.result.FourPrice;
import jp.sourceforge.orangesignal.ta.result.Histogram;
import jp.sourceforge.orangesignal.ta.result.Ichimoku;
import jp.sourceforge.orangesignal.ta.result.MESA;
import jp.sourceforge.orangesignal.ta.result.Shinohara;
import jp.sourceforge.orangesignal.ta.result.Step;
import jp.sourceforge.orangesignal.ta.result.Stochastics;


/**
 * <p>テクニカル分析用ユーティリティクラスを提供します。</p>
 * 
 * @author 杉澤 浩二
 */
public class TechnicalAnalysis {

	/**
	 * デフォルトコンストラクタです。
	 */
	protected TechnicalAnalysis() {}

	/* ---------------------------------------------------------------------- */
	/* 基本演算 */

	/**
	 * <p>二つのデータの和を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 和
	 */
	public static Number[] add(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = a[i].doubleValue() + b[i].doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの和を返します。</p>
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 和
	 */
	public static Number[] add(final Number[] a, final Number b) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = a[i].doubleValue() + b.doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの差を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 差
	 */
	public static Number[] sub(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = a[i].doubleValue() - b[i].doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの差を返します。</p>
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 差
	 */
	public static Number[] sub(final Number[] a, final Number b) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = a[i].doubleValue() - b.doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの積を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 積
	 */
	public static Number[] mult(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = a[i].doubleValue() * b[i].doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの積を返します。</p>
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 積
	 */
	public static Number[] mult(final Number[] a, final Number b) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = a[i].doubleValue() * b.doubleValue();
		}
		return results;
	}

	/**
	 * <p>二つのデータの商を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。<br>
	 * また、いずれかのデータが <code>0</code> の場合、計算結果は常に <code>0</code> となります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 商
	 */
	public static Number[] div(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			final double _a = a[i].doubleValue();
			final double _b = b[i].doubleValue();
			if (_a == 0.0 || _b == 0.0)
				results[i] = 0.0;
			else
				results[i] = _a / _b;
		}
		return results;
	}

	/**
	 * <p>二つのデータの商を返します。</p>
	 * いずれかのデータが <code>0</code> の場合、計算結果は常に <code>0</code> となります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 商
	 */
	public static Number[] div(final Number[] a, final Number b) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			final double _a = a[i].doubleValue();
			final double _b = b.doubleValue();
			if (_a == 0.0 || _b == 0.0)
				results[i] = 0.0;
			else
				results[i] = _a / _b;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから平均値を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 平均値
	 */
	public static Number[] avg(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = (a[i].doubleValue() + b[i].doubleValue()) * 0.5;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから平均値を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @param c データ3
	 * @return 平均値
	 */
	public static Number[] avg(final Number[] a, final Number[] b, final Number[] c) {
		final int length = getMinLength(a, b, c);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] != null && b[i] != null && c[i] != null) {
				final double sum = a[i].doubleValue() + b[i].doubleValue() + c[i].doubleValue();
				if (sum == 0.0)
					results[i] = 0.0;
				else
					results[i] = sum / 3;
			}
		}
		return results;
	}

	/**
	 * <p>指定されたデータから平均値を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @param c データ3
	 * @param d データ4
	 * @return 平均値
	 */
	public static Number[] avg(final Number[] a, final Number[] b, final Number[] c, final Number[] d) {
		final int length = getMinLength(a, b, c, d);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] != null && b[i] != null && c[i] != null && d[i] != null)
				results[i] = (a[i].doubleValue() + b[i].doubleValue() + c[i].doubleValue() + d[i].doubleValue()) * 0.25;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから正弦(サイン)を返します。</p>
	 * 
	 * @param a データ
	 * @return 正弦(サイン)
	 */
	public static Number[] sin(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.sin(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから余弦(コサイン)を返します。</p>
	 * 
	 * @param a データ
	 * @return 余弦(コサイン)
	 */
	public static Number[] cos(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.cos(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから正接(タンジェント)を返します。</p>
	 * 
	 * @param a データ
	 * @return 正接(タンジェント)
	 */
	public static Number[] tan(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.tan(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから逆正弦(アークサイン)を返します。</p>
	 * 
	 * @param a データ
	 * @return 逆正弦(アークサイン)
	 */
	public static Number[] asin(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.asin(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから逆余弦(アークコサイン)を返します。</p>
	 * 
	 * @param a データ
	 * @return 逆余弦(アークコサイン)
	 */
	public static Number[] acos(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.acos(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから逆正接(アークタンジェント)を返します。</p>
	 * 
	 * @param a データ
	 * @return 逆正接(アークタンジェント)
	 */
	public static Number[] atan(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.atan(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>オイラー数 e を指定された数列値で累乗した値を返します。</p>
	 * 
	 * @param a データ
	 * @return e<sup><code>a</code></sup>
	 */
	public static Number[] exp(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.exp(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから自然対数値を返します。</p>
	 * 
	 * @param a データ
	 * @return 自然対数値
	 */
	public static Number[] log(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.log(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから 10 を底とする対数を返します。</p>
	 * 
	 * @param a データ
	 * @return 10 を底とする対数
	 */
	public static Number[] log10(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.log10(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから平方根を返します。</p>
	 * 
	 * @param a データ
	 * @return 平方根
	 */
	public static Number[] sqrt(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.sqrt(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから小数を切り上げて返します。</p>
	 * 
	 * @param a データ
	 * @return 指定されたデータの小数を切り上げた値列
	 */
	public static Number[] ceil(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.ceil(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから少数を切り捨てて返します。</p>
	 * 
	 * @param a データ
	 * @return 指定されたデータの小数を切り捨てた値列
	 */
	public static Number[] floor(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.floor(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから四捨五入を返します。</p>
	 * 
	 * @param a データ
	 * @return 指定されたデータの四捨五入した値列
	 */
	public static Number[] round(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.round(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから絶対値を返します。</p>
	 * 
	 * @param x データ
	 * @return 絶対値
	 */
	public static Number[] abs(final Number[] a) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null)
				continue;
			results[i] = Math.abs(a[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから大きい方を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 大きい方の値
	 */
	public static Number[] max(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = Math.max(a[i].doubleValue(), b[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから小さい方を返します。</p>
	 * 各データの長さが異なる場合、計算結果は長さが短いデータと同じ長さになります。
	 * 
	 * @param a データ1
	 * @param b データ2
	 * @return 小さい方の値
	 */
	public static Number[] min(final Number[] a, final Number[] b) {
		final int length = getMinLength(a, b);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (a[i] == null || b[i] == null)
				continue;
			results[i] = Math.min(a[i].doubleValue(), b[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから双曲線正弦を返します。</p>
	 * 
	 * @param x データ
	 * @return 双曲線正弦
	 */
	public static Number[] sinh(final Number[] x) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (x[i] == null)
				continue;
			results[i] = Math.sinh(x[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから双曲線余弦を返します。</p>
	 * 
	 * @param x データ
	 * @return 双曲線余弦
	 */
	public static Number[] cosh(final Number[] x) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (x[i] == null)
				continue;
			results[i] = Math.cosh(x[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから双曲線正接を返します。</p>
	 * 
	 * @param x データ
	 * @return 双曲線正接
	 */
	public static Number[] tanh(final Number[] x) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (x[i] == null)
				continue;
			results[i] = Math.tanh(x[i].doubleValue());
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 期間指定付き基本演算 */

	/**
	 * <p>指定されたデータから指定された期間毎の合計を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 期間毎の合計
	 */
	public static Number[] sum(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;
			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum = sum + x[j].doubleValue();
			}
			results[i] = sum;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから指定された期間中の最高値 (Highest Value) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 最高値 (Highest Value)
	 */
	public static Number[] highest(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			Number high = null;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				if (high == null || high.doubleValue() < x[j].doubleValue())
					high = x[j];
			}
			results[i] = high;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから指定された期間中の最安値 (Lowest Value) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 最安値 (Lowest Value)
	 */
	public static Number[] lowest(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			Number low = null;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				if (low == null || low.doubleValue() > x[j].doubleValue())
					low = x[j];
			}
			results[i] = low;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから指定された期間中の最高値からの経過期間を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 最高値経過期間
	 */
	public static Number[] periodsSinceHighest(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			Number high = null;
			int since = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				if (high == null || high.doubleValue() < x[j].doubleValue()) {
					high = x[j];
					since = j;
				}
			}
			results[i] = i - since;
		}
		return results;
	}

	/**
	 * <p>指定されたデータの指定された期間中の最安値からの経過期間を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 最安値経過期間
	 */
	public static Number[] periodsSinceLowest(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			Number low = null;
			int since = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				if (low == null || low.doubleValue() > x[j].doubleValue()) {
					low = x[j];
					since = j;
				}
			}
			results[i] = i - since;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから Mid Point を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return Mid Point
	 */
	public static Number[] midpoint(final Number[] x, final int period) {
		return avg(highest(x, period), lowest(x, period));
	}

	/**
	 * <p>指定されたデータから Mid Price を返します。</p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period 期間
	 * @return Mid Price
	 */
	public static Number[] midprice(final Number[] high, final Number[] low, final int period) {
		return avg(highest(high, period), lowest(low, period));
	}

	/* ---------------------------------------------------------------------- */
	/* 統計関数 */

	/**
	 * <p>指定されたデータから分散 (Variance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param biased 母集団数式にバイアスをかけるかどうか
	 * @return 分散 (Variance)
	 */
	protected static Number[] var(final Number[] x, final int period, final boolean biased) {
		// 標本サイズを求めます。
		final int n = biased ? period : period - 1;
		if (n <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				sum = sum + x[j].doubleValue();
			}
			// 標本平均(相加平均)を求めます。…単純移動平均と同じ
			final double mean = sum == 0.0 ? 0.0 : sum / period;

			// 偏差平方和(sum squared)を求めます。
			double ss = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null)
					continue;
				// 偏差(平均 - 偏差)の2乗を加算します。
				ss = ss + Math.pow(mean - x[j].doubleValue(), 2);
			}
			results[i] = ss == 0.0 ? 0.0 : ss / n;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから不偏分散 (Unbiased Variance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 不偏分散 (Unbiased Variance)
	 */
	public static Number[] var(final Number[] x, final int period) {
		return var(x, period, false);
	}

	/**
	 * <p>指定されたデータから標本分散 (Biased Variance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 標本分散 (Biased Variance)
	 */
	public static Number[] varp(final Number[] x, final int period) {
		return var(x, period, true);
	}

	/**
	 * <p>指定されたデータから不偏分散 (Unbiased Variance) の平方根である、不偏標準偏差 (Unbiased Standard Deviation) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 不偏標準偏差 (Unbiased Standard Deviation)
	 */
	public static Number[] stddev(final Number[] x, final int period) {
		return sqrt(var(x, period));
	}

	/**
	 * <p>指定されたデータから標本分散 (Biased Variance) の平方根である、標本標準偏差 (Biased Standard Deviation) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 標本標準偏差 (Biased Standard Deviation)
	 */
	public static Number[] stddevp(final Number[] x, final int period) {
		return sqrt(varp(x, period));
	}

	/**
	 * <p>指定されたデータから共分散 (Covariance) を返します。</p>
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @param biased 母集団数式にバイアスをかけるかどうか
	 * @return 共分散 (Covariance)
	 */
	protected static Number[] covar(final Number[] x, final Number[] y, final int period, final boolean biased) {
		final int n = biased ? period : period - 1;
		if (n <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x, y);
		final Number[] results = new Number[length];

		for (int i = period - 1; i < length; i++) {
			double sum_x = 0;
			double sum_y = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum_x = sum_x + x[j].doubleValue();
				if (y[j] != null)
					sum_y = sum_y + y[j].doubleValue();
			}
			// 標本平均(相加平均)を求めます。…単純移動平均と同じ
			final double mean_x = sum_x / period;
			final double mean_y = sum_y / period;

			// 偏差積和を求めます。
			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] == null || y[j] == null)
					continue;
				sum = sum + (mean_x - x[j].doubleValue()) * (mean_y - y[j].doubleValue());
			}

			results[i] = sum == 0.0 ? 0.0 : sum / n;
		}
		return results;
	}

	public static Number[] covar(final Number[] x, final Number[] y, final int period) {
		return covar(x, y, period, true);
	}

	public static Number[] covarn(final Number[] x, final Number[] y, final int period) {
		return covar(x, y, period, false);
	}

	/**
	 * <p>指定されたデータから相関係数 (Correlation Coefficient) を返します。</p>
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @param biased 母集団数式にバイアスをかけるかどうか
	 * @return 相関係数 (Correlation Coefficient)
	 */
	public static Number[] correl(final Number[] x, final Number[] y, final int period, final boolean biased) {
		// 標本サイズを求めます。
		final int n = biased ? period : period - 1;
		if (n <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x, y);
		final Number[] results = new Number[length];

		for (int i = period - 1; i < length; i++) {
			double sum_x = 0;
			double sum_y = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum_x = sum_x + x[j].doubleValue();
				if (y[j] != null)
					sum_y = sum_y + y[j].doubleValue();
			}
			// 標本平均(相加平均)を求めます。…単純移動平均と同じ
			final double mean_x = sum_x / period;
			final double mean_y = sum_y / period;

			// xとyの偏差平方和(sum squared)及び偏差積和を求めます。
			double ss_x = 0;
			double ss_y = 0;
			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					ss_x = ss_x + Math.pow(mean_x - x[j].doubleValue(), 2);
				if (y[j] != null)
					ss_y = ss_y + Math.pow(mean_y - y[j].doubleValue(), 2);
				if (x[j] != null && y[j] != null)
					sum = sum + (mean_x - x[j].doubleValue()) * (mean_y - y[j].doubleValue());
			}

			if (sum == 0.0 || ss_x == 0.0 || ss_y == 0.0) {
				results[i] = 0.0;
			} else {
				final double cov = sum / n;					// 共分散
				final double stddev_x = Math.sqrt(ss_x / n);	// x の標準偏差
				final double stddev_y = Math.sqrt(ss_y / n);	// y の標準偏差
				results[i] = cov / (stddev_x * stddev_y);
			}
		}
		return results;
	}

	/**
	 * <p>指定されたデータから相関係数 (Correlation Coefficient) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #correl(Number[], Number[], int, boolean)} を母集団数式にバイアスをかけるとして呼出すだけです。
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @return 相関係数 (Correlation Coefficient)
	 * @see TechnicalAnalysis#correl(Number[], Number[], int, boolean)
	 */
	public static Number[] correl(final Number[] x, final Number[] y, final int period) {
		return correl(x, y, period, true);
	}

	/**
	 * <p>指定されたデータから相関係数 (Correlation Coefficient) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #correl(Number[], Number[], int, boolean)} を母集団数式にバイアスをかけるとして呼出すだけです。
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @return 相関係数 (Correlation Coefficient)
	 * @see TechnicalAnalysis#correl(Number[], Number[], int, boolean)
	 */
	public static Number[] pearson(final Number[] x, final Number[] y, final int period) {
		return correl(x, y, period, true);
	}

	/**
	 * <p>指定されたデータから線形回帰トレンド (Linear Regression Channel Lines) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param reserved 
	 * @return 線形回帰トレンド (Linear Regression Channel Lines)
	 * @deprecated このメソッドのインタフェースや実装内容は将来的に変更される可能性があります。
	 */
	public static Map<Bands5, Number[]> lr(final Number[] x, final int period, final int reserved) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		Number[] upper2	= new Number[length];
		Number[] upper1	= new Number[length];
		Number[] lr	= new Number[length];
		Number[] lower1	= new Number[length];
		Number[] lower2	= new Number[length];

		final int end = Math.max(length - reserved, 0);
		final int start = Math.max(end - period, 0);
		int y = 0;

		double sum_x = 0;
		double sum_y = 0;
		for (int i = start; i < end; i++) {
			if (x[i] != null)
				sum_x = sum_x + x[i].doubleValue();
			y++;
			sum_y = sum_y + y;
		}
		// 標本平均(相加平均)を求めます。…単純移動平均と同じ
		final int p = y;
		final double mean_x = sum_x / p;
		final double mean_y = sum_y / p;

		// 切片 b を算出
		double sum_xy = 0;
		double ss_y = 0;
		y = 0;
		for (int i = start; i < end; i++) {
			y++;
			final double dy = y - mean_y;	// y の偏差
			ss_y = ss_y + Math.pow(dy, 2);
			if (x[i] != null) {
				final double dx = x[i].doubleValue() - mean_x;	// x の偏差
				sum_xy = sum_xy + (dy * dx);
			}
		}
		final double b = sum_xy / ss_y;

		// 傾き a を算出
		final double a = mean_x - b * mean_y;

		// y = a + bx 算出
		double _sum = 0;
		double _x = 0;
		for (int i = start; i < end; i++) {
			final double value = a + b * (i - start);
			lr[i] = value;
			_x = _x + Math.pow(value, 2);
			_sum = _sum + value;
		}

		// 標準偏差を求めます
		final double sd = Math.sqrt((p * _x - Math.pow(_sum, 2)) / (p * (p - 1)));
		//final double sd = Math.sqrt(_x / p - Math.pow(_sum / p, 2));

		for (int i = start; i < end; i++) {
			if (lr[i] == null)
				continue;
			final double value = lr[i].doubleValue();
			upper2[i] = value + sd * 2;
			upper1[i] = value + sd;
			lower1[i] = value - sd;
			lower2[i] = value - sd * 2;
		}

		final Map<Bands5, Number[]> map = new EnumMap<Bands5, Number[]>(Bands5.class);
		map.put(Bands5.UPPER_BAND2, upper2);
		map.put(Bands5.UPPER_BAND1, upper1);
		map.put(Bands5.MIDDLE_BAND, lr);
		map.put(Bands5.LOWER_BAND1, lower1);
		map.put(Bands5.LOWER_BAND2, lower2);
		return map;
	}

	/**
	 * <p>指定されたデータからベータ値 (Beta) を返します。</p>
	 * 
	 * @param roc_x 比較する個別変動率データ
	 * @param roc_y 基準とする全体変動率データ
	 * @param period 期間
	 * @param biased 母集団数式にバイアスをかけるかどうか
	 * @return ベータ値 (Beta)
	 */
	@Deprecated
	protected static Number[] beta(final Number[] roc_x, final Number[] roc_y, final int period, final boolean biased) {
		// 標本サイズを求めます。
		final int n = biased ? period : period - 1;
		if (n <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(roc_x, roc_y);
		final Number[] results = new Number[length];

		// Linear Regression Slope と同じ？

		for (int i = period - 1; i < length; i++) {
			double sum_x = 0;
			double sum_y = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (roc_x[j] != null)
					sum_x = sum_x + roc_x[j].doubleValue();
				if (roc_y[j] != null)
					sum_y = sum_y + roc_y[j].doubleValue();
			}
			// 標本平均(相加平均)を求めます。…単純移動平均と同じ
			final double mean_x = sum_x / period;
			final double mean_y = sum_y / period;

			// xとyの偏差平方和(sum squared)及び偏差積和を求めます。
//			double ss_x = 0;
			double ss_y = 0;
			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
//				if (roc_x[j] != null)
//					ss_x = ss_x + Math.pow(mean_x - roc_x[j].doubleValue(), 2);
				if (roc_y[j] != null)
					ss_y = ss_y + Math.pow(mean_y - roc_y[j].doubleValue(), 2);
				if (roc_x[j] != null && roc_y[j] != null)
					sum = sum + (mean_x - roc_x[j].doubleValue()) * (mean_y - roc_y[j].doubleValue());
			}

			if (sum == 0.0 /*|| ss_x == 0.0*/ || ss_y == 0.0) {
				results[i] = 0.0;
			} else {
				final double cov = sum / n;					// 共分散
				//final double stddev_x = Math.sqrt(ss_x / n);	// x の標準偏差
				//final double stddev_y = Math.sqrt(ss_y / n);	// y の標準偏差
				// β = 比較の標準偏差 / 基準の標準偏差 * 相関係数
				//results[i] = stddev_x / stddev_y * (cov / (stddev_x * stddev_y));
				// この式を紐解くと β = 共分散 / 基準の分散 となる。
				results[i] = cov / (ss_y / n);
			}
		}
		return results;
	}

	/**
	 * <p>指定されたデータからベータ (Beta Coefficient) を返します。</p>
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @return ベータ (Beta Coefficient)
	 */
	@Deprecated
	public static Number[] beta(final Number[] x, final Number[] y, final int period) {
		return beta(roc(x, 1, PercentageScale.RATE), roc(y, 1, PercentageScale.RATE), period, true);
	}

	/**
	 * <p>指定されたデータからベータ2 (Beta Coefficient II) を返します。</p>
	 * 
	 * @param x データ1
	 * @param y データ2
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return ベータ2 (Beta Coefficient II)
	 */
	@Deprecated
	public static Number[] beta2(final Number[] x, final Number[] y, final int period, final MovingAverage matype) {
		return beta(
					ma(roc(x, 1, PercentageScale.RATE), period, matype),
					ma(roc(y, 1, PercentageScale.RATE), period, matype),
					period, true
				);
	}

	/* ---------------------------------------------------------------------- */
	/* ヒルベルト変換 (Hilbert Transform) */

	/* ---------------------------------------------------------------------- */
	/* 価格 */

	/**
	 * <p>指定されたデータから平均価格 (Average Price) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #avg(Number[], Number[], Number[], Number[])} を呼出すだけです。
	 * <pre>
	 * 平均価格＝(始値＋高値＋安値＋終値)÷4
	 * </pre>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return 平均価格 (Average Price)
	 * @see TechnicalAnalysis#avg(Number[], Number[], Number[], Number[])
	 */
	public static Number[] avgprice(final Number[] open, final Number[] high, final Number[] low, final Number[] close) {
		return avg(open, high, low, close);
	}

	/**
	 * 指定されたデータから平均足 (コマ足) を返します。
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return 平均足 (コマ足)
	 */
	public static Map<FourPrice, Number[]> heikin(final Number[] open, final Number[] high, final Number[] low, final Number[] close) {
		if (open == null || high == null || low == null || close == null)
			return null;

		final Number[] o = open.clone();
		final Number[] h = high.clone();
		final Number[] l = low.clone();
		final Number[] c = close.clone();

		final Number[] avg = avg(open, high, low, close);
		final int length = avg.length;
		final int start = indexOfNotNull(avg);

		double _o = avg[start].doubleValue();
		double _c = avg[start + 1].doubleValue();
		o[start + 1] = _o;
		c[start + 1] = _c;

		for (int i = start + 2; i < length; i++) {
			if (avg[i] == null)
				continue;
			_o = (_o + _c) * 0.5;
			_c = avg[i].doubleValue();
			o[i] = _o;
			c[i] = _c;
		}

		final Map<FourPrice, Number[]> results = new EnumMap<FourPrice, Number[]>(FourPrice.class);
		results.put(FourPrice.OPEN, o);
		results.put(FourPrice.HIGH, h);
		results.put(FourPrice.LOW, l);
		results.put(FourPrice.CLOSE, c);
		return results;
	}

	/**
	 * <p>指定されたデータから中央価格 (Median Price) を返します。</p>
	 * <pre>
	 * 中央価格＝(高値＋安値)÷2
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @return 中央価格 (Median Price)
	 */
	public static Number[] mp(final Number[] high, final Number[] low) {
		return avg(high, low);
	}

	/**
	 * <p>指定されたデータから代表価格 (ティピカル プライス / Typical Price) を返します。</p>
	 * <pre>
	 * 計算式
	 * 代表価格＝(高値＋安値＋終値)÷3
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return 代表価格 (ティピカル プライス / Typical Price)
	 */
	public static Number[] tp(final Number[] high, final Number[] low, final Number[] close) {
		return avg(high, low, close);
	}

	/**
	 * <p>指定されたデータから加重終値 (Weighted Close) を返します。</p>
	 * <pre>
	 * 加重終値＝(高値＋安値＋終値×2)÷4
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return 加重終値 (Weighted Close)
	 */
	public static Number[] wtcl(final Number[] high, final Number[] low, final Number[] close) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (high[i] == null || low[i] == null || close[i] == null)
				continue;
			results[i] = (high[i].doubleValue() + low[i].doubleValue() + close[i].doubleValue() * 2) * 0.25;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから真高値 (トゥルー ハイ / True High) を返します。</p>
	 * <pre>
	 * TRH＝前日終値と当日高値のどちらか高い方
	 * </pre>
	 * 
	 * @param high 高値
	 * @param close 終値
	 * @return 真高値 (トゥルー ハイ / True High)
	 */
	public static Number[] th(final Number[] high, final Number[] close) {
		final int length = getMinLength(high, close);
		final Number[] results = new Number[length];

		if (high[0] != null)
			results[0] = high[0].doubleValue();

		for (int i = 1; i < length; i++) {
			if (high[i] == null)
				continue;

			if (close[i - 1] == null && high[i] != null)
				results[i] = high[i].doubleValue();
			else
				results[i] = Math.max(high[i].doubleValue(), close[i - 1].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから真安値 (トゥルー ロー / True Low) を返します。</p>
	 * <pre>
	 * TRL＝前日終値と当日安値のどちらか低い方
	 * </pre>
	 * 
	 * @param low 安値
	 * @param close 終値
	 * @return 真安値 (トゥルー ロー / True Low)
	 */
	public static Number[] tl(final Number[] low, final Number[] close) {
		final int length = getMinLength(low, close);
		final Number[] results = new Number[length];

		if (low[0] != null)
			results[0] = low[0].doubleValue();

		for (int i = 1; i < length; i++) {
			if (low[i] == null)
				continue;

			if (close[i - 1] == null && low[i] != null)
				results[i] = low[i].doubleValue();
			else
				results[i] = Math.min(low[i].doubleValue(), close[i - 1].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータから真値幅 (トゥルー レンジ / True Range) を返します。</p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return 真値幅 (トゥルー レンジ / True Range)
	 */
	public static Number[] tr(final Number[] high, final Number[] low, final Number[] close) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];

		if (high[0] != null && low[0] != null)
			results[0] = high[0].doubleValue() - low[0].doubleValue();

		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (high[i] == null || low[i] == null || close[j] == null)
				continue;

			final double _h = high[i].doubleValue();
			final double _l = low[i].doubleValue();
			final double _c = close[j].doubleValue();

			// 実質的な変動幅(TR)を求めます。
			final double hl = _h - _l;
			final double hc = _h - _c;
			final double cl = _c - _l;
			results[i] = Math.max(Math.max(hl, hc), cl);
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 移動平均 */

	/**
	 * <p>指定されたデータから指定された移動平均 (Moving Average) を返します。</p>
	 * このメソッドは利便性の為に提供しています。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return 移動平均 (Moving Average)
	 */
	public static Number[] ma(final Number[] x, final int period, final MovingAverage matype) {
		switch (matype) {
			case SMA:
				return sma(x, period);
			case SMMA:
				return smma(x, period);
			case GMA:
				return gma(x, period);
			case RMA:
				return rma(x, period);
			case WWMA:
				return wwma(x, period);
			case WMA:
				return wma(x, period);
			case HMA:
				return hma(x, period);
			case TMA:
				return tma(x, period);
			case EMA:
				return ema(x, period);
			case DEMA:
				return dema(x, period);
			case TEMA:
				return tema(x, period);
			case ZLEMA:
				return zlema(x, period);
			case EPMA:
				return epma(x, period);
			case T3:
				return t3(x, period);
			default:
				throw new RuntimeException();
		}
	}

	/**
	 * <p>指定されたデータから指定された移動平均を二重化して返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #doubleSmooth(Number[], int, MovingAverage, int, MovingAverage)} を呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return 二重化された移動平均
	 * @see TechnicalAnalysis#ma(Number[], int, MovingAverage)
	 */
	public static Number[] doubleSmooth(final Number[] x, final int period, final MovingAverage matype) {
		return doubleSmooth(x, period, matype, period, matype);
	}

	/**
	 * <p>指定されたデータから指定された移動平均を二重化して返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #ma(Number[], int, MovingAverage)} を期間1と期間2で二回呼出すだけです。
	 * 
	 * @param x データ
	 * @param period1 期間1
	 * @param matype1 期間1の移動平均の種類
	 * @param period2 期間2
	 * @param matype2 期間2の移動平均の種類
	 * @return 二重化された移動平均
	 * @see TechnicalAnalysis#ma(Number[], int, MovingAverage)
	 */
	public static Number[] doubleSmooth(
			final Number[] x,
			final int period1, final MovingAverage matype1,
			final int period2, final MovingAverage matype2)
	{
		return ma(ma(x, period1, matype1), period2, matype2);
	}

	/**
	 * <p>指定されたデータから指定された移動平均を三重化して返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #tripleSmooth(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)} を呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return 三重化された移動平均
	 * @see TechnicalAnalysis#tripleSmooth(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)
	 */
	public static Number[] tripleSmooth(final Number[] x, final int period, final MovingAverage matype) {
		return tripleSmooth(x, period, matype, period, matype, period, matype);
	}

	/**
	 * <p>指定されたデータから指定された移動平均を三重化して返します。</p>
	 * 
	 * @param x データ
	 * @param period1 期間1
	 * @param matype1 期間1の移動平均の種類
	 * @param period2 期間2
	 * @param matype2 期間2の移動平均の種類
	 * @param period3 期間3
	 * @param matype3 期間3の移動平均の種類
	 * @return 三重化された移動平均
	 */
	public static Number[] tripleSmooth(
			final Number[] x,
			final int period1, final MovingAverage matype1,
			final int period2, final MovingAverage matype2,
			final int period3, final MovingAverage matype3)
	{
		return ma(ma(ma(x, period1, matype1), period2, matype2), period3, matype3);
	}

	/**
	 * <p>指定されたデータからずらした移動平均 (Displaced Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param displaced オフセット
	 * @return ずらした移動平均 (Displaced Moving Average)
	 */
	public static Number[] dma(final Number[] x, final int period, final int displaced) {
		return dma(x, period, MovingAverage.SMA, displaced);
	}

	/**
	 * <p>指定されたデータからずらした移動平均 (Displaced Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @param displaced オフセット
	 * @return ずらした移動平均 (Displaced Moving Average)
	 */
	public static Number[] dma(final Number[] x, final int period, final MovingAverage matype, final int displaced) {
		return displace(ma(x, period, matype), displaced);
	}

	/**
	 * <p>指定されたデータから単純移動平均 (Simple Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 単純移動平均 (Simple Moving Average)
	 */
	public static Number[] sma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum = sum + x[j].doubleValue();
			}
			results[i] = sum / period;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから平滑移動平均 (Smoothed Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 平滑移動平均 (Smoothed Moving Average)
	 */
	public static Number[] smma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		// TODO: 要確認
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum = sum + x[j].doubleValue();
			}

			final double sma = sum / period;
			results[i] = (sum - sma + x[i].doubleValue()) / period;
		}
		return results;
	}

	/**
	 * 指定されたデータから幾何学移動平均 (Geometric Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 幾何学移動平均 (Geometric Moving Average)
	 */
	@Deprecated
	public static Number[] gma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		final double n = 1.0 / period;
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double sum = 1.0;
			final int end = i - period + 1;
			for (int j = i; j >= end; j--) {
				if (x[j] != null)
					sum = sum * x[j].doubleValue();
			}
			results[i] = Math.pow(sum, n);
		}
		return results;
	}

	/**
	 * 指定されたデータから修正移動平均 (Running Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 修正移動平均 (Running Moving Average)
	 */
	public static Number[] rma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		// 前のRMA値
		Number rma = null;
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			if (rma == null) {
				// 1日目(単純移動平均)
				double sum = 0;
				for (int j = i - period + 1; j <= i; j++) {
					if (x[j] != null)
						sum = sum + x[j].doubleValue();
				}
				results[i] = sum / period;
				rma = results[i];
			} else {
				// 2日目以降
				results[i] = ((period - 1) * rma.doubleValue() + x[i].doubleValue()) / period;
				rma = results[i];
			}
		}
		return results;
	}

	/**
	 * <p>指定されたデータからワイルダー移動平均 (Welles Wilder's Moving Average) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #rma(Number[], int)} を呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return ワイルダー移動平均 (Welles Wilder's Moving Average)
	 * @see TechnicalAnalysis#rma(Number[], int)
	 */
	public static Number[] wwma(final Number[] x, final int period) {
		return rma(x, period);
	}

	/**
	 * 指定されたデータから加重移動平均 (Weighted Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 加重移動平均 (Weighted Moving Average)
	 */
	public static Number[] wma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double num = 0;	// 分子
			double den = 0;	// 分母
			double w = 1;		// 重み

			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					num = num + w * x[j].doubleValue();
				den = den + w;
				w = w + 1;
			}
			results[i] = num / den;
		}
		return results;
	}

	/**
	 * 指定されたデータからハル移動平均 (Hull's Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return ハル移動平均 (Hull's Moving Average)
	 */
	public static Number[] hma(final Number[] x, final int period) {
		final Number[] wma1 = wma(x, (int) Math.floor(period * 0.5));
		final Number[] wma2 = wma(x, period);

		final int length = getMinLength(wma1, wma2);
		final Number[] y = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (wma1[i] == null || wma2[i] == null)
				continue;
			y[i] = 2 * wma1[i].doubleValue() - wma2[i].doubleValue();
		}

		return wma(y, (int) Math.floor(Math.sqrt(period)));
	}

	/**
	 * 指定されたデータから三角移動平均 (Triangular Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 三角移動平均 (Triangular Moving Average)
	 */
	public static Number[] tma(final Number[] x, final int period) {
		return doubleSmooth(x, (int) Math.ceil((period + 1.0) * 0.5), MovingAverage.SMA);
	}

	/**
	 * 指定されたデータから指数平滑移動平均 (Exponential Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 指数移動平均 (Exponential Moving Average)
	 */
	public static Number[] ema(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		// 平滑化定数
		final double a = 2.0 / (period + 1.0);
		// 前のEMA値
		Number ema = null;

		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			if (ema == null) {
				// 1日目
				double sum = 0;
				for (int j = i - period + 1; j <= i; j++) {
					if (x[j] != null)
						sum = sum + x[j].doubleValue();
				}
				results[i] = sum / period;
				ema = results[i];
			} else {
				// 2日目以降 (どちらの式でもよい)
				//results[i] = ema.doubleValue() * (period - 1) / (period + 1) + source[i].doubleValue() * a;
				results[i] = ema.doubleValue() + a * (x[i].doubleValue() - ema.doubleValue());
				ema = results[i];
			}
		}
		return results;
	}

	/**
	 * 指定されたデータから二重指数平滑移動平均 (Double Exponential Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 二重指数移動平均 (Double Exponential Moving Average)
	 */
	// 1994 Patrick Mulloy
	public static Number[] dema(final Number[] x, final int period) {
		// DEMA　＝　〔２×EMA〕－〔（EMAのEMA）〕
		final Number[] ema = ema(x, period);
		final Number[] ema2 = ema(ema, period);
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (ema[i] == null || ema2[i] == null)
				continue;
			results[i] = 2.0 * ema[i].doubleValue() - ema2[i].doubleValue();
		}
		return results;
//		return doubleSmooth(x, period, MovingAverage.EMA);
	}

	/**
	 * 指定されたデータから三重指数平滑移動平均 (Triple Exponential Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 三重指数移動平均 (Triple Exponential Moving Average)
	 */
	// 1994 Patrick Mulloy
	public static Number[] tema(final Number[] x, final int period) {
		// TEMA　＝　〔３×EMA〕－〔３×（EMAのEMA）〕＋〔（EMAのEMAのEMA）〕
		final Number[] ema = ema(x, period);
		final Number[] ema2 = ema(ema, period);
		final Number[] ema3 = ema(ema2, period);
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (ema[i] == null || ema2[i] == null || ema3 == null)
				continue;
			results[i] = 3.0 * ema[i].doubleValue() - 3.0 * ema2[i].doubleValue() + ema3[i].doubleValue();
		}
		return results;
//		return tripleSmooth(x, period, MovingAverage.EMA);
	}

	/**
	 * 指定されたデータから零ラグ指数平滑移動平均 (Zero Lag Exponential Moving Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 零ラグ指数移動平均 (Zero Lag Exponential Moving Average)
	 */
	public static Number[] zlema(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		// 平滑化定数
		final double k = 2.0 / (period + 1.0);
		// ラグ期間
		final int lag = (period - 1) / 2;
		// 前のEMA値
		Number ema = null;

		for (int i = period - 1; i < length; i++) {
			if (x[i] == null || x[i - lag] == null)
				continue;

			if (ema == null) {
				// 1日目
				double sum = 0;
				for (int j = i - period + 1; j <= i; j++) {
					if (x[j] != null)
						sum = sum + x[j].doubleValue();
				}
				ema = sum / period;
			} else {
				// 2日目以降 (どちらの式でもよい)
				//results[i] = ema.doubleValue() * (period - 1) / (period + 1) + source[i].doubleValue() * a;
				ema = k * (2.0 * x[i].doubleValue() - x[i - lag].doubleValue()) + (1.0 - k) * ema.doubleValue();
			}
			results[i] = ema;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからエンドポイント移動平均 (Endpoint Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return エンドポイント移動平均 (Endpoint Moving Average)
	 */
	public static Number[] epma(final Number[] x, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		final double n = 2.0 / (period * (period + 1.0));
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double sum = 0;
			double pos = 1;
			for (int j = i - period + 1; j <= i; j++) {
				if (x[j] != null)
					sum = sum + ((3 * pos) - period - 1) * x[j].doubleValue();
				pos = pos + 1;
			}
			results[i] = n * sum;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからティルソン T3 移動平均 (Tillson's T3 Moving Average) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #t3(Number[], int, double)} を係数 <code>0.7</code> で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return ティルソン T3 移動平均 (Tillson's T3 Moving Average)
	 * @see TechnicalAnalysis#t3(Number[], int, double)
	 */
	public static Number[] t3(final Number[] x, final int period) {
		return t3(x, period, 0.7);
	}

	/**
	 * <p>指定されたデータからティルソン T3 移動平均 (Tillson's T3 Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param a 係数
	 * @return ティルソン T3 移動平均 (Tillson's T3 Moving Average)
	 */
	public static Number[] t3(final Number[] x, final int period, final double a) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		//final Number[] e1 = ema(x, period);
		//final Number[] e2 = ema(e1, period);
		//final Number[] e3 = ema(e2, period);
		final Number[] e3 = tripleSmooth(x, period, MovingAverage.EMA);
		final Number[] e4 = ema(e3, period);
		final Number[] e5 = ema(e4, period);
		final Number[] e6 = ema(e5, period);

		final double c1 = -Math.pow(a, 3);
		final double c2 =  3 * Math.pow(a, 2) + 3 * Math.pow(a, 3);
		final double c3 = -6 * Math.pow(a, 2) - 3 * a - 3 * Math.pow(a, 3);
		final double c4 =  1 + 3 * a + Math.pow(a, 3) + 3 * Math.pow(a, 2);

		for (int i = period - 1; i < length; i++) {
			if (e3[i] == null || e4[i] == null || e5[i] == null || e6[i] == null)
				continue;
			results[i] = c1 * e6[i].doubleValue() + c2 * e5[i].doubleValue() + c3 * e4[i].doubleValue() + c4 * e3[i].doubleValue();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから MESA 適応移動平均 (MESA Adaptive Moving Average) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #mama(Number[], double, double)} を短期リミット <code>0.5</code>、
	 * 長期リミット <code>0.05</code> で呼出すだけです。
	 * 
	 * @param x データ
	 * @return MESA 適応移動平均 (MESA Adaptive Moving Average)
	 * @see TechnicalAnalysis#mama(Number[], double, double)
	 */
	public static Map<MESA, Number[]> mama(final Number[] x) {
		return mama(x, 0.5, 0.05);
	}

	/**
	 * <p>指定されたデータから MESA 適応移動平均 (MESA Adaptive Moving Average) を返します。</p>
	 * 
	 * @param x データ
	 * @param limit_fast 短期リミット
	 * @param limit_slow 長期リミット
	 * @return MESA 適応移動平均 (MESA Adaptive Moving Average)
	 */
	public static Map<MESA, Number[]> mama(final Number[] x, final double limit_fast, final double limit_slow) {
		final int length = getMinLength(x);
		final Number[] mama = new Number[length];
		final Number[] fama = new Number[length];

		final double[] smooth = new double[length];
		final double[] period = new double[length];
		final double[] detrender = new double[length];
		final double[] q1 = new double[length];
		final double[] i1 = new double[length];
		final double[] ji = new double[length];
		final double[] jq = new double[length];
		final double[] i2 = new double[length];
		final double[] q2 = new double[length];
		final double[] re = new double[length];
		final double[] im = new double[length];
		final double[] smoothPeriod = new double[length];
		final double[] phase = new double[length];

		// 前の MAMA & FAMA 値
		double _mama = 0;
		double _fama = 0;

		final double rad2deg = 180.0 / (4.0 * Math.atan(1));

		for (int i = 6; i < length; i++) {
			if (x[i] == null || x[i - 1] == null || x[i - 2] == null || x[i - 3] == null)
				continue;

			final double p = 0.075 * period[i - 1] + 0.54;

			// smooth
			smooth[i] = (4 * x[i].doubleValue() + 3 * x[i - 1].doubleValue() + 2 * x[i - 2].doubleValue() + x[i - 3].doubleValue()) * 0.1;
			// detrender
			detrender[i] = (0.0962 * smooth[i] + 0.5769 * smooth[i - 2] - 0.5769 * smooth[i - 4] - 0.0962 * smooth[i - 6]) * p;

			// compute InPhase and Quadrature components
			q1[i] = (0.0962 * detrender[i] + 0.5769 * detrender[i - 2] - 0.5769 * detrender[i - 4] - 0.0962 * detrender[i - 6]) * p;
			i1[i] = detrender[i - 3];

			// Advance the phase of I1 and Q1 by 90 degrees
			ji[i] = (0.0962 * i1[i] + 0.5769 * i1[i - 2] - 0.5769 * i1[i - 4] - 0.0962 * i1[i - 6]) * p; 
			jq[i] = (0.0962 * q1[i] + 0.5769 * q1[i - 2] - 0.5769 * q1[i - 4] - 0.0962 * q1[i - 6]) * p;

			// Phasor addition for 3 bar averaging
			i2[i] = i1[i] - jq[i];
			q2[i] = q1[i] + ji[i];

			// Smooth the I and Q components before applying the discriminator
			i2[i] = 0.2 * i2[i] + 0.8 * i2[i - 1];
			q2[i] = 0.2 * q2[i] + 0.8 * q2[i - 1];

			// Homodyne Discriminator
			re[i] = i2[i] * i2[i - 1] + q2[i] * q2[i - 1];
			im[i] = i2[i] * q2[i - 1] - q2[i] * i2[i - 1];
			re[i] = 0.2 * re[i] + 0.8 * re[i - 1];
			im[i] = 0.2 * im[i] + 0.8 * im[i - 1];

			if (im[i] != 0.0 && re[i] != 0.0)		period[i] = 360.0 / (Math.atan(im[i] / re[i]) * rad2deg);
			if (period[i] > 1.5 * period[i - 1])	period[i] = 1.5 * period[i - 1];
			if (period[i] < 0.67 * period[i - 1])	period[i] = 0.67 * period[i - 1];
			if (period[i] < 6)						period[i] = 6;
			if (period[i] > 50)						period[i] = 50;
			period[i] = 0.2 * period[i] + 0.8 * period[i - 1];
			smoothPeriod[i] = 0.33 * period[i] + 0.67 * smoothPeriod[i - 1];
			if (i1[i] != 0)							phase[i] = Math.atan(q1[i] / i1[i]) * rad2deg;    

			double deltaPhase = phase[i - 1] - phase[i];
			if (deltaPhase < 1) deltaPhase = 1;

			double alpha = limit_fast / deltaPhase ;
			if (alpha < limit_slow) alpha = limit_slow;

			_mama = alpha * x[i].doubleValue() + (1 - alpha) * _mama;
			_fama = 0.5 * alpha * _mama + (1 - 0.5 * alpha) * _fama;

			mama[i] = _mama;
			fama[i] = _fama;
		}

		final Map<MESA, Number[]> map = new EnumMap<MESA, Number[]>(MESA.class);
		map.put(MESA.MAMA, mama);
		map.put(MESA.FAMA, fama);
		return map;
	}

	/**
	 * 指定されたデータから出来高加重移動平均 (Volume Weighted Moving Average) を返します。
	 * 
	 * @param price 価格データ
	 * @param volume 出来高データ
	 * @param period 期間
	 * @return 出来高加重移動平均 (Volume Weighted Moving Average)
	 */
	public static Number[] vwma(final Number[] price, final Number[] volume, final int period) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(price, volume);
		final Number[] results = new Number[length];

		for (int i = period - 1; i < length; i++) {
			if (price[i] == null || volume[i] == null)
				continue;

			double num = 0;	// 分子
			double den = 0;	// 分母

			for (int j = i - period + 1; j <= i; j++) {
				if (price[j] == null || volume[j] == null)
					continue;
				num = num + price[j].doubleValue() * volume[j].doubleValue();
				den = den + volume[j].doubleValue();
			}
			results[i] = num / den;
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* ゴールデンクロス / デッドクロス */

	/**
	 * <p>指定されたデータからゴールデンクロス、デッドクロス (Golden Cross / Dead Cross) などシグナルの種類を列挙して返します。</p>
	 * 
	 * @param fast 短期データ
	 * @param slow 長期データ
	 * @return シグナルの種類を列挙
	 */
	public static CrossSignal[] cross(final Number[] fast, final Number[] slow) {
		final int length = getMinLength(fast, slow);
		final CrossSignal[] results = new CrossSignal[length];
		Number prev_fast = null;
		Number prev_slow = null;
		for (int i = 0; i < length; i++) {
			if (prev_slow != null && prev_fast != null && slow[i] != null && fast[i] != null) {
				if ((prev_slow.doubleValue() < prev_fast.doubleValue()) && (slow[i].doubleValue() > fast[i].doubleValue())) {
					results[i] = CrossSignal.DEAD_CROSS;
				} else if ((prev_slow.doubleValue() > prev_fast.doubleValue()) && (slow[i].doubleValue() < fast[i].doubleValue())) {
					results[i] = CrossSignal.GOLDEN_CROSS;
				}
			}
			prev_fast = fast[i];
			prev_slow = slow[i];
		}
		return results;
	}

	/**
	 * <p>指定されたデータからゴールデンクロス、デッドクロス (Golden Cross / Dead Cross) などシグナルの種類を列挙して返します。</p>
	 * 
	 * @param fast 短期データ
	 * @param slow 長期データ
	 * @return シグナルの種類を列挙
	 * @since 1.1
	 */
	public static CrossSignal[] cross(final Number[] fast, final double slow) {
		final int length = getMinLength(fast);
		final CrossSignal[] results = new CrossSignal[length];
		Number prev_fast = null;
		for (int i = 0; i < length; i++) {
			if (prev_fast != null && fast[i] != null) {
				if ((slow < prev_fast.doubleValue()) && (slow > fast[i].doubleValue())) {
					results[i] = CrossSignal.DEAD_CROSS;
				} else if ((slow > prev_fast.doubleValue()) && (slow < fast[i].doubleValue())) {
					results[i] = CrossSignal.GOLDEN_CROSS;
				}
			}
			prev_fast = fast[i];
		}
		return results;
	}

	/**
	 * <p>指定されたデータからゴールデンクロス (Golden Cross) かどうかを列挙して返します。</p>
	 * 
	 * @param fast 短期データ
	 * @param slow 長期データ
	 * @return ゴールデンクロス (Golden Cross) かどうかを列挙
	 * @deprecated このメソッドは下位互換性の為に提供している為、将来的に削除される可能性があります。代わりに {@link #cross(Number[], Number[])} を使用して下さい。
	 */
	public static final boolean[] gc(final Number[] fast, final Number[] slow) {
		final CrossSignal[] cross = cross(fast, slow);
		final int length = cross.length;
		final boolean[] results = new boolean[length];
		for (int i = 0; i < length; i++)
			results[i] = cross[i] != null && cross[i] == CrossSignal.GOLDEN_CROSS;
		return results;
	}

	/**
	 * <p>指定されたデータからデッドクロス (Dead Cross) かどうかを列挙して返します。</p>
	 * 
	 * @param fast 短期データ
	 * @param slow 長期データ
	 * @return デッドクロス (Dead Cross) かどうかを列挙
	 * @deprecated このメソッドは下位互換性の為に提供している為、将来的に削除される可能性があります。代わりに {@link #cross(Number[], Number[])} を使用して下さい。
	 */
	public static final boolean[] dc(final Number[] fast, final Number[] slow) {
		final CrossSignal[] cross = cross(fast, slow);
		final int length = cross.length;
		final boolean[] results = new boolean[length];
		for (int i = 0; i < length; i++)
			results[i] = cross[i] != null && cross[i] == CrossSignal.DEAD_CROSS;
		return results;
	}

	/* ---------------------------------------------------------------------- */

	/**
	 * 指定されたデータからジグザグ (ZigZag) を返します。
	 * 
	 * @param x データ
	 * @param rate 定率値幅(パーセント)
	 * @return ジグザグ (ZigZag)
	 */
	public static Number[] zigzag(final Number[] x, final double rate) {
		// swing high
		// swing low
		final double r = rate * 0.01;
		final int length = x.length;
		final Number[] results = new Number[length];

		final int start = indexOfNotNull(x);
		if ((start + 1) >= (length - 1))
			return results;

		double highest = x[start].doubleValue();
		int highest_index = start;
		double lowest = x[start].doubleValue();
		int lowest_index = start;
		boolean upTrend = x[start].doubleValue() < x[start + 1].doubleValue();
		results[start] = x[start].doubleValue();

		for (int i = start + 1; i < length; i++) {
			if (x[i] == null)
				continue;

			final double value = x[i].doubleValue();

			if (upTrend) {
				if (value > highest) {
					// 高値を更新します。
					highest = value;
					highest_index = i;
				} else if (r <= ((highest - value) / highest)) {
					// 陰転
					results[highest_index] = highest;
					final double d = (highest - lowest) / (highest_index - lowest_index);
					for (int j = lowest_index + 1; j < highest_index; j++)
						results[j] = lowest + d * (j - lowest_index);
					lowest = value;
					lowest_index = i;
					upTrend = false;
				}
			} else {
				if (value < lowest) {
					// 安値を更新します。
					lowest = value;
					lowest_index = i;
				} else if (r <= ((value - lowest) / lowest)) {
					// 陽転
					results[lowest_index] = lowest;
					final double d = (highest - lowest) / (lowest_index - highest_index);
					for (int j = highest_index + 1; j < lowest_index; j++)
						results[j] = highest - d * (j - highest_index);
					highest = value;
					highest_index = i;
					upTrend = true;
				}
			}
		}

		final int last_index = lastIndexOfNotNull(x);
		double _x = x[last_index].doubleValue();
		results[last_index] = _x;

		if (upTrend) {
			final double d = (_x - lowest) / (last_index - lowest_index);
			for (int j = lowest_index + 1; j < last_index; j++)
				results[j] = lowest + d * (j - lowest_index);
		} else {
			final double d = (highest - _x) / (last_index - highest_index);
			for (int j = highest_index + 1; j < last_index; j++)
				results[j] = highest - d * (j - highest_index);
		}

		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 価格バンド */

	/**
	 * <p>指定されたデータからボリンジャーバンド (Bollinger Bands) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、指定された高値、安値、終値から真値幅(TP)を求めた後、
	 * 単に {@link #bbands(Number[], int, double)} を呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param rate 係数
	 * @return ボリンジャーバンド (Bollinger Bands)
	 * @see TechnicalAnalysis#bbands(Number[], Number[], Number[], int, double)
	 */
	// ジョン・ボリンジャー
	public static Map<Bands5, Number[]> bbands(final Number[] high, final Number[] low, final Number[] close, final int period, final double rate) {
		return bbands(avg(high, low, close), period, rate);
	}

	/**
	 * <p>指定されたデータからボリンジャーバンド (Bollinger Bands) を返します。</p>
	 * 
	 * @param tp 真値幅(TP)
	 * @param period 期間
	 * @param rate 係数
	 * @return ボリンジャーバンド (Bollinger Bands)
	 */
	// ジョン・ボリンジャー
	public static Map<Bands5, Number[]> bbands(final Number[] tp, final int period, final double rate) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(tp);
		Number[] upper2	= new Number[length];
		Number[] upper1	= new Number[length];
		Number[] tpma	= new Number[length];
		Number[] lower1	= new Number[length];
		Number[] lower2	= new Number[length];

		for (int i = period; i < length; i++) {
			if (tp[i] == null)
				continue;

			double s = 0;
			double sum = 0;

			for (int j = i - period + 1; j <= i; j++) {
				if (tp[j] == null)
					continue;
				final double _tp = tp[j].doubleValue();
				s = s + Math.pow(_tp, 2);
				sum = sum + _tp;
			}

			// 移動平均
			final double ma = sum / period;
			// 標準偏差
			double sd = Math.sqrt((period * s - Math.pow(sum, 2)) / (period * (period - 1)));
			if (Double.isNaN(sd))
				sd = 0;

			upper2[i] = ma + (rate * 2.0) * sd;
			upper1[i] = ma + (rate * 1.0) * sd;
			tpma[i] = ma;
			lower1[i] = ma - (rate * 1.0) * sd;
			lower2[i] = ma - (rate * 2.0) * sd;
		}

		final Map<Bands5, Number[]> map = new EnumMap<Bands5, Number[]>(Bands5.class);
		map.put(Bands5.UPPER_BAND2, upper2);
		map.put(Bands5.UPPER_BAND1, upper1);
		map.put(Bands5.MIDDLE_BAND, tpma);
		map.put(Bands5.LOWER_BAND1, lower1);
		map.put(Bands5.LOWER_BAND2, lower2);
		return map;
	}

	/**
	 * <p>指定されたデータからエンベロープ (Envelope) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @param rate 値幅(%)
	 * @return エンベロープ (Envelope)
	 * @see TechnicalAnalysis#envelope(Number[], double)
	 */
	public static Map<Bands5, Number[]> envelope(final Number[] x, final int period, final MovingAverage matype, final double rate) {
		final Number[] ma = ma(x, period, matype);
		final Map<Bands5, Number[]> map = new EnumMap<Bands5, Number[]>(Bands5.class);
		map.put(Bands5.MIDDLE_BAND, ma);
		map.put(Bands5.UPPER_BAND1, envelope(ma, rate *  1.0));
		map.put(Bands5.UPPER_BAND2, envelope(ma, rate *  2.0));
		map.put(Bands5.LOWER_BAND1, envelope(ma, rate * -1.0));
		map.put(Bands5.LOWER_BAND2, envelope(ma, rate * -2.0));
		return map;
	}

	/**
	 * <p>指定されたデータからエンベロープ (Envelope) を返します。</p>
	 * 
	 * @param ma 移動平均
	 * @param rate 値幅(%)
	 * @return エンベロープ (Envelope)
	 */
	public static Number[] envelope(final Number[] ma, final double rate) {
		final int length = getMinLength(ma);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (ma[i] == null)
				continue;
			results[i] = ma[i].doubleValue() * (100.0 + rate) / 100.0;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから一目均衡表 (Ichimoku Kinko Hyo) を返します。</p>
	 * <p>
	 * 一目均衡表は、昭和の初め、一目山人(いちもくさんじん)により完成された相場そのものについての書です。
	 * 一目均衡表における相場理論は、三大骨子(波動論、時間論、値幅観測論)に基づいて展開されていますが、
	 * 「相場は値幅ではなくて時間である」という一目山人の言葉の通り、時間論を軸としています。
	 * つまり、相場の主体はあくまでも時間であり、価格は結果として従う、というのが一目均衡表の出発点です。
	 * </p>
	 * <pre>
	 * 基準線＝(過去26日間の最高値＋過去26日間の最安値)÷2
	 * 転換線＝(過去9日間の最高値＋過去9日間の最安値)÷2
	 * 先行スパン1＝(転換線+基準線)÷2を26日後へずらす
	 * 先行スパン2＝(過去52日間の最高値＋過去52日間の最安値)÷2を26日後へずらす
	 * 遅行スパン＝当日終値を26日前へずらす
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_kijun 基準期間
	 * @param period_tenkan 転換期間
	 * @param period_span スパン期間
	 * @return 一目均衡表 (Ichimoku Kinko Hyo)
	 * @see TechnicalAnalysis#highest(Number[], int)
	 * @see TechnicalAnalysis#lowest(Number[], int)
	 * @see TechnicalAnalysis#displace(Number[], int)
	 */
	public static Map<Ichimoku, Number[]> ichimoku(
		final Number[] high, final Number[] low, final Number[] close,
		final int period_kijun, final int period_tenkan, final int period_span)
	{
		if (period_kijun <= 0)
			throw new IllegalArgumentException();
		if (period_tenkan <= 0)
			throw new IllegalArgumentException();
		if (period_span <= 0)
			throw new IllegalArgumentException();

		final Number[] kijun	= avg(highest(high, period_kijun), lowest(low, period_kijun));
		final Number[] tenkan	= avg(highest(high, period_tenkan), lowest(low, period_tenkan));
		final Number[] highest	= highest(high, period_span * 2);
		final Number[] lowest	= lowest(low, period_span * 2);

		final Map<Ichimoku, Number[]> map = new EnumMap<Ichimoku, Number[]>(Ichimoku.class);
		map.put(Ichimoku.KIJUN, kijun);
		map.put(Ichimoku.TENKAN, tenkan);
		map.put(Ichimoku.SENKOU1, displace(avg(tenkan, kijun), (period_span - 1)));
		map.put(Ichimoku.SENKOU2, displace(avg(highest, lowest), (period_span - 1)));
		map.put(Ichimoku.CHIKOU, displace(close, -(period_span - 1)));
		return map;
	}

	/**
	 * 指定されたデータを指定されたオフセット分ずらします。
	 * 
	 * @param array データ
	 * @param offset オフセット
	 * @return オフセット分ずらしたデータ
	 */
	public static Number[] displace(final Number[] array, final int offset) {
		if (array == null)
			return null;

		final int length = array.length;
		final Number[] results = new Number[length];
		if (offset > 0) {
			System.arraycopy(array, 0, results, offset, length - offset);
		} else {
			final int n = Math.abs(offset);
			System.arraycopy(array, n, results, 0, length - n);
		}
		return results;
	}

	/**
	 * <p>指定されたデータからピボット (Pivot Point) を返します。</p>
	 * <p>
	 * ピボットとは「回転軸」を意味し、日々の市場価格がそのポイントを中心に回転(振幅)することを前提としたデイトレーダー向け(短期売買向け)のテクニカル指標です。
	 * 別名「リアクショントレンドシステム」と呼ばれ、J ウエルズ ワイルダー ジュニア (J. Welles Wilder, Jr.) 氏が考案したものとされています。
	 * </p>
	 * <pre>
	 * PIVOT＝(前日高値＋前日安値＋前日終値)÷3
	 * HBOP＝PIVOT×2＋前日高値－前日安値×2
	 * S2＝PIVOT＋前日高値－前日安値
	 * S1＝PIVOT×2－前日安値
	 * B1＝PIVOT×2－前日高値
	 * B2＝PIVOT－前日高値＋前日安値
	 * LBOP＝PIVOT×2－前日高値×2＋前日安値
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return ピボット (Pivot Point)
	 */
	public static Map<Bands7, Number[]> pivot(final Number[] high, final Number[] low, final Number[] close) {
		final int length = getMinLength(high, low, close);
		final Number[] r3 = new Number[length];
		final Number[] r2 = new Number[length];
		final Number[] r1 = new Number[length];
		final Number[] pp = new Number[length];
		final Number[] s1 = new Number[length];
		final Number[] s2 = new Number[length];
		final Number[] s3 = new Number[length];
		for (int i = 1; i < length; i++) {
			if (high[i - 1] == null || low[i - 1] == null || close[i - 1] == null)
				continue;

			final double h = high[i - 1].doubleValue();
			final double l = low[i - 1].doubleValue();
			final double p = (h + l + close[i - 1].doubleValue()) / 3;

			pp[i] = p;
			r3[i] = p * 2 + h - l * 2;
			r2[i] = p + h - l;
			r1[i] = p * 2 - l;
			s1[i] = p * 2 - h;
			s2[i] = p - h + l;
			s3[i] = p * 2 - h * 2 + l;
		}
		final Map<Bands7, Number[]> map = new EnumMap<Bands7, Number[]>(Bands7.class);
		map.put(Bands7.UPPER_BAND3, r3);
		map.put(Bands7.UPPER_BAND2, r2);
		map.put(Bands7.UPPER_BAND1, r1);
		map.put(Bands7.MIDDLE_BAND, pp);
		map.put(Bands7.LOWER_BAND1, s1);
		map.put(Bands7.LOWER_BAND2, s2);
		map.put(Bands7.LOWER_BAND3, s3);
		return map;
	}

	/**
	 * <p>指定されたデータからパラボリック (Parabolic SAR) を返します。</p>
	 * <p>
	 * パラボリックは放物線という意味で、J ウエルズ ワイルダー ジュニア (J. Welles Wilder, Jr.) 氏が考案した、SAR (ストップ&amp;リバースポイント) と呼ばれるラインを用いたトレンドフォロー系テクニカル指標です。
	 * </p>
	 * <pre>
	 * SAR＝前日SAR＋AF×(EP－前日SAR)
	 * EP…極大値 (買い期間はその期間の最高値、売り期間はその期間の最安値)
	 * AF…加速係数 (通常は 0.02≦AF≦0.20) 
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param factor 係数
	 * @return パラボリック (Parabolic SAR)
	 */
	public static Number[] sar(final Number[] high, final Number[] low, final Number[] close, final double factor) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];

		// 加速因子の増分です。
		final double AF_STEP = 0.02;
		// 加速因子の最大値です。
		final double AF_MAX = 0.2;

		final int start = indexOfNotNull(close);
		if (start == -1)
			return results;
		if ((length - start) < 2)
			return results;

		// 買い/売りポジションを判断します。
		boolean upTrend = close[start].doubleValue() < close[start + 1].doubleValue();
		// SAR (Stop And Reverse Point)
		double sar = upTrend ? low[start].doubleValue() : high[start].doubleValue();
		// EP (Extreme Point) ... 極大値
		double ep = upTrend ? high[start + 1].doubleValue() : low[start + 1].doubleValue();
		double af = factor;	// AF (Acceleration Factor) ... 加速因子 [0.02～0.2]

		results[1] = sar;

		// 翌日SAR = 当日SAR + AF × (EP - 当日SAR)
		sar = sar + af * (ep - sar);

		for (int i = start + 2; i < length; i++) {
			if (low[i] == null || high[i] == null)
				continue;

			// 買い/売りポジションの転換を判断します。
			if (upTrend) {
				// 上げトレンド時は安値接触で転換
				if (sar > low[i].doubleValue()) {
					sar = ep;
					ep = low[i].doubleValue();
					af = factor;
					upTrend = false;
				// 高値更新
				} else if (ep < high[i].doubleValue()) {
					ep = high[i].doubleValue();
					af = Math.min(af + AF_STEP, AF_MAX);
				}
			} else {
				// 下げトレンド時は高値接触で転換
				if (sar < high[i].doubleValue()) {
					sar = ep;
					ep = high[i].doubleValue();
					af = factor;
					upTrend = true;
				// 安値更新
				} else if (ep > low[i].doubleValue()) {
					ep = low[i].doubleValue();
					af = Math.min(af + AF_STEP, AF_MAX);
				}
			}

			results[i] = sar;
			sar = sar + af * (ep - sar);
		}
		return results;
	}

	/**
	 * <p>指定されたデータからボラティリティー システムを返します。</p>
	 * <pre>
	 * VIUpper＝n日間の終値の最高値－(ATR×W)
	 * VILoer＝n日間の終値の最安値＋(ATR×W)
	 * ATR…真値幅平均
	 * W…重み係数 (2.0～2.9 が多く用いられる)
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param weight 重み係数
	 * @return ボラティリティー システム
	 * @see TechnicalAnalysis#atr(Number[], Number[], Number[], int)
	 */
	public static Map<Bands2, Number[]> volatility(
			final Number[] high, final Number[] low, final Number[] close,
			final int period, final double weight)
	{
		final int length = getMinLength(high, low, close);
		final Number[] upper = new Number[length];
		final Number[] lower = new Number[length];

		final Number[] atr = atr(high, low, close, period);
		final Number[] hvi = highest(close, period);
		final Number[] lvi = lowest(close, period);

		for (int i = 0; i < length; i++) {
			if (hvi[i] == null || atr[i] == null)
				continue;
			final double n = atr[i].doubleValue() * weight;
			upper[i] = hvi[i].doubleValue() + n;
			lower[i] = lvi[i].doubleValue() - n;
		}

		final Map<Bands2, Number[]> map = new EnumMap<Bands2, Number[]>(Bands2.class);
		map.put(Bands2.UPPER_BAND, upper);
		map.put(Bands2.LOWER_BAND, lower);
		return map;
	}

	/* ---------------------------------------------------------------------- */
	/* ボラティリティ指標 */

	/**
	 * 指定されたデータから真値幅平均 (Average True Range) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return 真値幅平均 (Average True Range)
	 */
	// 1978 J. Welles Wilder
	public static Number[] atr(final Number[] high, final Number[] low, final Number[] close, final int period) {
		return sma(tr(high, low, close), period);
	}

	/**
	 * <p>指定されたデータからチャイキン ボラティリティー (Chaikin's Volatility) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、百分率の尺度を {@link PercentageScale#PERCENT} で {@link #chv(Number[], Number[], int, int, PercentageScale)} を呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period_ma 移動平均の期間
	 * @param period_roc ROC の期間
	 * @return チャイキン ボラティリティー (Chaikin's Volatility)
	 * @see TechnicalAnalysis#chv(Number[], Number[], int, int, PercentageScale)
	 */
	// マーク・チャイキン
	public static Number[] chv(final Number[] high, final Number[] low, final int period_ma, final int period_roc) {
		return chv(high, low, period_ma, period_roc, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータからチャイキン ボラティリティー (Chaikin's Volatility) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period_ma 移動平均の期間
	 * @param period_roc ROC の期間
	 * @param unit 百分率の尺度
	 * @return チャイキン ボラティリティー (Chaikin's Volatility)
	 */
	// マーク・チャイキン
	public static Number[] chv(final Number[] high, final Number[] low, final int period_ma, final int period_roc, final PercentageScale unit) {
		return roc(ema(sub(high, low), period_ma), period_roc, unit);
	}

	/**
	 * 指定されたデータからヒストリカル ボラティリティー (Historical Volatility) を返します。
	 * 
	 * @param a データ
	 * @param period 期間
	 * @param days 日数
	 * @return ヒストリカル ボラティリティー (Historical Volatility)
	 */
	public static Number[] hv(final Number[] a, final int period, final int days) {
		final int length = getMinLength(a);
		final Number[] results = new Number[length];
		for (int i = period; i < length; i++) {
			if (a[i] == null)
				continue;

			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (a[j] == null || a[j - 1] == null)
					continue;
				final double d = a[j].doubleValue() / a[j - 1].doubleValue();
				sum = sum + Math.log(d);
			}
			final double ma = sum / period;

			sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (a[j] == null || a[j - 1] == null)
					continue;
				final double x = Math.log(a[j].doubleValue() / a[j - 1].doubleValue()) - ma;
				sum = sum + Math.pow(x, 2);
			}

			// 標準偏差
			results[i] = Math.sqrt(days * (sum / (period - 1))) * 100;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから標準化真値幅平均 (Normalized Average True Range) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、百分率の尺度を {@link PercentageScale#PERCENT} で {@link #natr(Number[], Number[], Number[], int, PercentageScale)} を呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return 標準化真値幅平均 (Normalized Average True Range)
	 * @see TechnicalAnalysis#natr(Number[], Number[], Number[], int, PercentageScale)
	 */
	public static Number[] natr(final Number[] high, final Number[] low, final Number[] close, final int period) {
		return natr(high, low, close, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから標準化真値幅平均 (Normalized Average True Range) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 標準化真値幅平均 (Normalized Average True Range)
	 */
	public static Number[] natr(final Number[] high, final Number[] low, final Number[] close, final int period, final PercentageScale unit) {
		final Number[] atr = atr(high, low, close, period);
		final int length = atr.length;
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (atr[i] == null || close[i] == null)
				continue;
			results[i] = atr[i].doubleValue() / close[i].doubleValue() * unit.scale();
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* モメンタム指標 */

	/**
	 * <p>指定されたデータから価格オシレーター (Absolute Price Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、移動平均の種類を {@link MovingAverage#SMA} で {@link #apo(Number[], int, int, MovingAverage)} を呼出すだけです。
	 * 
	 * @param prices 価格
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @return 価格オシレーター (Absolute Price Oscillator)
	 * @see TechnicalAnalysis#apo(Number[], int, int, MovingAverage)
	 */
	public static Number[] apo(final Number[] prices, final int fast, final int slow) {
		return apo(prices, fast, slow, MovingAverage.EMA);
	}

	/**
	 * 指定されたデータから価格オシレーター (Absolute Price Oscillator) を返します。
	 * 
	 * @param prices 価格
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @return 価格オシレーター (Absolute Price Oscillator)
	 */
	public static Number[] apo(final Number[] prices, final int fast, final int slow, final MovingAverage matype) {
		return sub(ma(prices, Math.min(fast, slow), matype), ma(prices, Math.max(fast, slow), matype));
	}

	/**
	 * <p>指定されたデータからアキュムレーション スイング インデックス (Accumulation Swing Index) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param t 値幅制限 (Limit move)
	 * @return アキュムレーション スイング インデックス (Accumulation Swing Index)
	 */
	// J.W.ワイルダー
	public static Number[] asi(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final double t) {
		// ASI　＝　（前日のASI）　＋　（当日のSI）
		// データの一番最初では前日のASIがないため、当日のSIを用いることになります
		final Number[] si = si(open, high, low, close, t);
		final int length = si.length;
		final Number[] results = new Number[length];
		Number asi = null;
		for (int i = 1; i < length; i++) {
			if (si[i] == null)
				continue;
			if (asi == null)
				asi = si[i];
			asi = asi.doubleValue() + si[i].doubleValue();
			results[i] = asi;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからスイング インデックス (Swing Index) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param t 値幅制限 (Limit move)
	 * @return スイング インデックス (Swing Index)
	 */
	// -100 ～ +100
	// J.Welles Wilder
	private static Number[] si(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final double t) {
		// 50 * (前日終値 - 当日終値 + 0.5 * (前日終値 - 前日始値) + 0.25 * (当日終値 - 当日始値) / r) * (k / t)
		// k = 前日高値 - 当日終値 又は 前日安値 - 当日終値　※値幅が大きい方
		// r = 当日終値～前日高値、安値の値　の関係によって変化します。**
		final int length = getMinLength(open, high, low, close);
		final Number[] results = new Number[length];
		for (int i = 1; i < length; i++) {
			final int yestaday = i - 1;
			if (open[yestaday] == null || high[yestaday] == null || low[yestaday] == null || close[yestaday] == null || open[i] == null || high[i] == null || low[i] == null || close[i] == null)
				continue;

			final double oy = open[i-1].doubleValue();
//			final double hy = high[i-1].doubleValue();
//			final double ly = low[i-1].doubleValue();
			final double cy = close[i-1].doubleValue();
			final double o = open[i].doubleValue();
			final double h = high[i].doubleValue();
			final double l = low[i].doubleValue();
			final double c = close[i].doubleValue();

			// R を求めます。
			final double hc = h - cy;
			final double lc = l - cy;
			final double hl = h - l;
			final double max = Math.max(Math.max(hc, lc), hl);
			final double r;
			if (max == hc) {
				r = (h - cy) - 0.5 * (l - cy) + 0.25 * (cy - oy);
			} else if (max == lc) {
				r = (l - cy) - 0.5 * (h - cy) + 0.25 * (cy - oy);
			} else if (max == hl) {
				r = (h - l) + 0.25 * (cy - oy);
			} else
				r = 0;

			// K を求めます。
			final double k = Math.max(h - cy, l - cy);
			// スイング インデックス
			results[i] = 50 * ((c - cy) + 0.5 * (c - o) + 0.25 * (cy - oy) / r) * (k / t);
			//results[i] = 50 * (cy - c + 0.5 * (cy - oy) + 0.25 * (c - o) / r) * (k / t);
		}
		return results;
	}

	/**
	 * <p>指定されたデータからアルーン (Aroon) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、百分率の尺度を {@link PercentageScale#PERCENT} で {@link #aroon(Number[], int, PercentageScale)} を呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return アルーン (Aroon)
	 * @see TechnicalAnalysis#aroon(Number[], int, PercentageScale)
	 */
	// Tushar Chande
	public static Map<Aroon, Number[]> aroon(final Number[] x, final int period) {
		return aroon(x, period, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータからアルーン (Aroon) を返します。</p>
	 * <p>
	 * アールンは トゥーシャー シャンデ (Tushar Chande) 氏が 1995 年に開発したテクニカル指標です。
	 * サンスクリット語 (古代・中世のインドの公用語) で「夜明けの曙光」を意味し、トレンドの是非、強さ、転換を計る指標です。
	 * </p>
	 * <pre>
	 * AroonUp＝(n－過去n日間の最高値からの経過期間)÷n×100
	 * AroonDown＝(n－過去n日間の最安値からの経過期間)÷n×100
	 * Aronn＝AroonUp－AroonDown
	 * </pre>
	 * 
	 * @param x データ
	 * @param period 期間(当日を含む)
	 * @param unit 百分率の尺度
	 * @return アルーン (Aroon)
	 * @see TechnicalAnalysis#periodsSinceHighest(Number[], int)
	 * @see TechnicalAnalysis#periodsSinceLowest(Number[], int)
	 */
	// Tushar Chande
	public static Map<Aroon, Number[]> aroon(final Number[] x, final int period, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] up = new Number[length];
		final Number[] down = new Number[length];
		final Number[] aroon = new Number[length];

		final Number[] periodH = periodsSinceHighest(x, period);
		final Number[] periodL = periodsSinceLowest(x, period);
		for (int i = 0; i < length; i++) {
			if (periodH[i] == null || periodL[i] == null)
				continue;
			up[i] = (period - periodH[i].doubleValue()) / period * unit.scale();
			down[i] = (period - periodL[i].doubleValue()) / period * unit.scale();
			aroon[i] = up[i].doubleValue() - down[i].doubleValue();
		}

		final Map<Aroon, Number[]> map = new EnumMap<Aroon, Number[]>(Aroon.class);
		map.put(Aroon.AROON_UP, up);
		map.put(Aroon.AROON_DOWN, down);
		map.put(Aroon.AROON, aroon);
		return map;
	}

	/**
	 * <p>指定されたデータから BMP (Balance of Market Power) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return BMP (Balance of Market Power)
	 * @see TechnicalAnalysis#bmp(Number[], Number[], Number[], Number[], PercentageScale)
	 */
	public static Number[] bmp(final Number[] open, final Number[] high, final Number[] low, final Number[] close) {
		return bmp(open, high, low, close, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから BMP (Balance of Market Power) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param unit 百分率の尺度
	 * @return BMP (Balance of Market Power)
	 * @see TechnicalAnalysis#bop(Number[], Number[], Number[], Number[], PercentageScale)
	 */
	public static Number[] bmp(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final PercentageScale unit) {
		return bop(open, high, low, close, unit);
	}

	/**
	 * <p>指定されたデータから BOP (Balance of Power) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return BOP (Balance of Power)
	 * @see TechnicalAnalysis#bop(Number[], Number[], Number[], Number[], PercentageScale)
	 */
	public static Number[] bop(final Number[] open, final Number[] high, final Number[] low, final Number[] close) {
		return bop(open, high, low, close, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから BOP (Balance of Power) を返します。</p>
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param unit 百分率の尺度
	 * @return BOP (Balance of Power)
	 */
	public static Number[] bop(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final PercentageScale unit) {
		final int length = getMinLength(open, high, low, close);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (open[i] == null || high[i] == null || low[i] == null || close[i] == null)
				continue;

			final double o = open[i].doubleValue();
			final double h = high[i].doubleValue();
			final double l = low[i].doubleValue();
			final double c = close[i].doubleValue();

			double thl = h - l;
			if (thl == 0.0) {
				thl = 0.00001;
			}

			// Reward_Based_On_Open
			final double BuRBoO = (h - o) / thl;	// bull
			final double BeRBoO = (o - l) / thl;	// bear
			// Reward_Based_On_Close
			final double BuRBoC = (c - l) / thl;	// bull
			final double BeRBoC = (h - c) / thl;	// bear

			// Reward_Based_On_Open_Close
			final double BuRBoOC;					// bull
			final double BeRBoOC;					// bear
			if (c > o) {
				BuRBoOC = (c - o) / thl;
				BeRBoOC = 0.0;
			} else {
				BuRBoOC = 0.0;
				BeRBoOC = (o - c) / thl;
			}

			results[i] = ((BuRBoO + BuRBoC + BuRBoOC) / 3 - (BeRBoO + BeRBoC + BeRBoOC) / 3) * unit.scale();
		}
		return results;
	}

	/**
	 * 指定されたデータから商品チャネル指数 (Commodity Channel Index) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return 商品チャネル指数 (Commodity Channel Index)
	 */
	// ドナルド・ランバート
	public static Number[] cci(final Number[] high, final Number[] low, final Number[] close, final int period) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];

		final Number[] avg = avg(high, low, close);
		final Number[] ma = sma(avg, period);

		for (int i = period; i < length; i++) {
			if (avg[i] == null || ma[i] == null)
				continue;

			double sum = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (avg[j] != null && ma[j] != null)
					sum = sum + Math.abs(avg[j].doubleValue() - ma[j].doubleValue());
			}

			if (sum == 0.0)
				results[i] = 0.0;
			else
				results[i] = (avg[i].doubleValue() - ma[i].doubleValue()) / (0.015 * (sum / period));
		}
		return results;
	}

	/**
	 * <p>指定されたデータから前日比(値幅) (Change) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #mom(Number[], int)} を期間 <code>1</code> で呼出すだけです。
	 * 
	 * @param x データ
	 * @return 前日比(値幅) (Change)
	 * @see TechnicalAnalysis#mom(Number[], int)
	 */
	public static final Number[] chg(final Number[] x) {
		return mom(x, 1);
	}

	/**
	 * 指定されたデータから続伸続落情報 (Positive &amp; Negative Changes Count) を返します。
	 * 
	 * @param x データ
	 * @param unit 百分率の尺度
	 * @return 続伸続落情報 (Positive &amp; Negative Changes Count)
	 */
	public static Map<Changes, Number[]> chgcnt(final Number[] x, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] count	= new Number[length];
		final Number[] close	= new Number[length];
		final Number[] change	= new Number[length];
		final Number[] percent	= new Number[length];

		final Number[] changes = chg(x);
		int base = -1;
		int orientation = 0;

		for (int i = 1; i < length; i++) {
			if (changes[i] == null) {
				base = -1;
				orientation = 0;
				continue;
			}

			final double chg = changes[i].doubleValue();
			if (chg > 0.0D) {
				// 高騰の場合
				if (orientation <= 0) {
					base = i - 1;
					orientation = 1;
				}
				count[i] = i - base;
				close[i] = x[base];
				final double baseClose = x[base].doubleValue();
				final double currentClose = x[i].doubleValue();
				final double countChange = currentClose - baseClose;
				change[i] = countChange;
				percent[i] = countChange / baseClose * unit.scale();
			} else if (chg < 0.0D) {
				// 下落の場合
				if (orientation >= 0) {
					base = i - 1;
					orientation = -1;
				}
				count[i] = (i - base) * -1;
				close[i] = x[base];
				final double baseClose = x[base].doubleValue();
				final double currentClose = x[i].doubleValue();
				final double countChange = currentClose - baseClose;
				change[i] = countChange;
				percent[i] = countChange / baseClose * unit.scale();
			} else {
				base = -1;
				orientation = 0;
				count[i] = 0;
				close[i] = x[i];
				change[i] = 0;
				percent[i] = 0;
			}
		}

		final Map<Changes, Number[]> map = new EnumMap<Changes, Number[]>(Changes.class);
		map.put(Changes.COUNT, count);
		map.put(Changes.CLOSE, close);
		map.put(Changes.CHANGE, change);
		map.put(Changes.PERCENT_CHANGE, percent);
		return map;
	}

	/**
	 * 指定されたデータからチャイキン マネー フロー (Chaikin's Money Flow) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @param period 期間
	 * @return チャイキン マネー フロー (Chaikin's Money Flow)
	 */
	public static Number[] cmf(final Number[] high, final Number[] low, final Number[] close, final Number[] volume, final int period) {
		final int length = getMinLength(high, low, close, volume);
		final Number[] results = new Number[length];

		final Number[] ad = sum(ad(high, low, close, volume), period);
		final Number[] v  = sum(volume, period);

		for (int i = period - 1; i < length; i++) {
			if (ad[i] == null || v[i] == null)
				continue;

			final double _ad = ad[i].doubleValue();
			final double _v  =  v[i].doubleValue();

			if (_ad == 0 || _v == 0)
				results[i] = 0;
			else
				results[i] = _ad / _v;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからチャンデ モメンタム オシレーター (Chande's Momentum Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #cmo(Number[], int, PercentageScale)} を百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比
	 * @param period 期間
	 * @return チャンデ モメンタム オシレーター (Chande's Momentum Oscillator)
	 * @see TechnicalAnalysis#cmo(Number[], int, PercentageScale)
	 */
	// TUSHAR CHANDE
	public static Number[] cmo(final Number[] change, final int period) {
		return cmo(change, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータからチャンデ モメンタム オシレーター (Chande's Momentum Oscillator) を返します。
	 * 
	 * @param change 前日比
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return チャンデ モメンタム オシレーター (Chande's Momentum Oscillator)
	 */
	// TUSHAR CHANDE
	public static Number[] cmo(final Number[] change, final int period, final PercentageScale unit) {
		if (period <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(change);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;

			double up   = 0;		// 値上り合計
			double down = 0;	// 値下り合計

			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null)
					continue;

				final double diff = change[j].doubleValue();
				if (diff > 0)
					up = up + diff;
				else if (diff < 0)
					down = down - diff;
			}

			final double numerator = up - down;
			final double denominator = up + down;
			if (numerator == 0 || denominator == 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = numerator / denominator * unit.scale();
		}
		return results;
	}

	/**
	 * 指定されたデータからコポック (Coppock) を返します。
	 * 
	 * @param x データ
	 * @param roc ROC の期間
	 * @param ma 移動平均の期間
	 * @param matype 移動平均の種類
	 * @return コポック (Coppock)
	 */
	public static Number[] coppock(final Number[] x, final int roc, final int ma, final MovingAverage matype) {
		return ma(roc(x, roc, PercentageScale.PERCENT), ma, matype);
	}

	/**
	 * <p>指定されたデータからコポック (Coppock) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #coppock(Number[], int, int, int, MovingAverage)} を短期 ROC の期間 <code>11</code>、
	 * 長期 ROC の期間 <code>14</code>、移動平均の期間 <code>10</code>、
	 * 移動平均の種類 {@link MovingAverage#WMA} で呼出すだけです。
	 * 
	 * @param x データ
	 * @return コポック (Coppock)
	 * @see TechnicalAnalysis#coppock(Number[], int, int, int, MovingAverage)
	 */
	public static Number[] coppock(final Number[] x) {
		return coppock(x, 11, 14, 10, MovingAverage.WMA);
	}

	/**
	 * 指定されたデータからコポック (Coppock) を返します。
	 * 
	 * @param x データ
	 * @param period_fast_roc 短期 ROC の期間
	 * @param period_slow_roc 長期 ROC の期間
	 * @param period_ma 移動平均の期間
	 * @param matype 移動平均の種類
	 * @return コポック (Coppock)
	 */
	public static Number[] coppock(final Number[] x, final int period_fast_roc, final int period_slow_roc, final int period_ma, final MovingAverage matype) {
		return ma(add(roc(x, period_fast_roc, PercentageScale.PERCENT), roc(x, period_slow_roc, PercentageScale.PERCENT)), period_ma, matype);
	}

	public static Number[] crsi(final Number[] change, final int period, final int period2) {
		return crsi(change, period, period2, period2, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータからカトラー相対力指数 (Cutler's Relative Strength Index) を返します。<p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #crsi(Number[], int, int, int, PercentageScale)} を百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @param period_gain 上昇集計期間
	 * @param period_loss 下降集計期間
	 * @return カトラー相対力指数 (Cutler's Relative Strength Index)
	 * @see #crsi(Number[], int, int, int, PercentageScale)
	 */
	public static Number[] crsi(final Number[] change, final int period, final int period_gain, final int period_loss) {
		return crsi(change, period, period_gain, period_loss, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータからカトラー相対力指数 (Cutler's Relative Strength Index) を返します。
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @param period_gain 上昇集計期間
	 * @param period_loss 下降集計期間
	 * @param unit 百分率の尺度
	 * @return カトラー相対力指数 (Cutler's Relative Strength Index)
	 */
	public static Number[] crsi(final Number[] change, final int period, final int period_gain, final int period_loss, final PercentageScale unit) {
		if (period <= 0)
			throw new IllegalArgumentException();
		if (period_gain < period)
			throw new IllegalArgumentException();
		if (period_loss < period)
			throw new IllegalArgumentException();

		final int length = getMinLength(change);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;

			double gain = 0;		// 値上り合計
			for (int j = i - period_gain + 1; j <= i; j++) {
				if (change[j] == null)
					continue;
				final double diff = change[j].doubleValue();
				if (diff > 0)
					gain = gain + diff;
			}

			double loss = 0;	// 値下り合計
			for (int j = i - period_loss + 1; j <= i; j++) {
				if (change[j] == null)
					continue;
				final double diff = change[j].doubleValue();
				if (diff < 0)
					loss = loss - diff;
			}

//			results[i] = 100 - 100  / (1 + Math.abs(up) / Math.abs(down));
			final double updown = gain + loss;
			if (updown <= 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = gain / updown * unit.scale();
		}
		return results;
	}

	/**
	 * 指定されたデータから商品選択指数 (Commodity Selection Index) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period DI 及び ATR の期間
	 * @param period_adx ADX の期間
	 * @param v コンバージョンファクター(倍率)
	 * @param m マージン(証拠金)
	 * @param c コミッション(手数料)
	 * @return 商品選択指数 (Commodity Selection Index)
	 */
	// J. ウェルズ・ワイルダー
	public static Number[] csi(
			final Number[] high, final Number[] low, final Number[] close,
			final int period, final int period_adx,
			final double v, final double m, final double c)
	{
		final Number[] adxr = dmi(high, low, close, period, period_adx).get(DMI.ADX_R);
		final Number[] atr = atr(high, low, close, period);
		final double k = ((v / m) * (1 / 150 + c)) * 100;
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (adxr[i] == null || atr[i] == null)
				continue;
			results[i] = adxr[i].doubleValue() * atr[i].doubleValue() * k;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから方向性指数 (Directional Movement Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #dmi(Number[], Number[], Number[], int, int, PercentageScale)} を百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_di DI の期間
	 * @param period_adx ADX の期間
	 * @return 方向性指数 (Directional Movement Index)
	 * @see TechnicalAnalysis#dmi(Number[], Number[], Number[], int, int, PercentageScale)
	 */
	// Tushar Chande
	public static Map<DMI, Number[]> dmi(final Number[] high, final Number[] low, final Number[] close, final int period_di, final int period_adx) {
		return dmi(high, low, close, period_di, period_adx, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから方向性指数 (Directional Movement Index) を返します。</p>
	 * <p>
	 * この指標は J ウエルズ ワイルダー ジュニア (J.Welles Wilder, Jr.) 氏によって開発された指標で、市場がどれだけの方向性のある動きをしているか、もしくはトレンドが市場に存在するのかを測定する指標です。
	 * </p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_di DI の期間
	 * @param period_adx ADX の期間
	 * @param unit 百分率の尺度
	 * @return 方向性指数 (Directional Movement Index)
	 */
	// Tushar Chande 
	public static Map<DMI, Number[]> dmi(
			final Number[] high, final Number[] low, final Number[] close,
			final int period_di, final int period_adx, final PercentageScale unit)
	{
		if (period_di <= 0)
			throw new IllegalArgumentException();
		if (period_adx <= 0)
			throw new IllegalArgumentException();

		final int length = getMinLength(high, low, close);
		if (length < 2)
			throw new IllegalArgumentException();

		// +DM、-DM、TR、DX を求めます。
		final Number[] pdm = new Number[length];	// 上昇幅(+DM)
		final Number[] mdm = new Number[length];	// 下落幅(-DM)
		final Number[] dx = new Number[length];	// DX

		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (high[i] == null || high[j] == null)
				continue;
			if (low[i] == null || low[j] == null)
				continue;
			if (close[i] == null || close[j] == null)
				continue;

			// 上昇幅(+DM)、下降幅(-DM)を求めます。
			double p = high[i].doubleValue() - high[j].doubleValue();
			double m = low[j].doubleValue() - low[i].doubleValue();

			if (p < 0 && m < 0) {
				p = 0;
				m = 0;
			} else if (p < m) {
				p = 0;
			} else if (p > m) {
				m = 0;
			} else /* if (p == m) */ {
				p = 0;
				m = 0;
			}

			pdm[i] = p;
			mdm[i] = m;
		}

		// 実質的な変動幅(TR)
		final Number[] tr = tr(high, low, close);

		// +DI、-DI を求めます。
		final Number[] pdi = new Number[length];
		final Number[] mdi = new Number[length];

		for (int i = period_di; i < length; i++) {
			double sum_pdm = 0;
			double sum_mdm = 0;
			double sum_tr = 0;
			for (int j = i - period_di + 1; j <= i; j++) {
				if (pdm[j] != null)
					sum_pdm = sum_pdm + pdm[j].doubleValue();
				if (mdm[j] != null)
					sum_mdm = sum_mdm + mdm[j].doubleValue();
				if (tr[j] != null)
					sum_tr = sum_tr + tr[j].doubleValue();
			}

			if (sum_pdm == 0 || sum_tr == 0)
				pdi[i] = 0;
			else
				pdi[i] = sum_pdm / sum_tr * unit.scale();

			if (sum_mdm == 0 || sum_tr == 0)
				mdi[i] = 0;
			else
				mdi[i] = sum_mdm / sum_tr * unit.scale();

			double v1 = Math.abs(pdi[i].doubleValue() - mdi[i].doubleValue());
			double v2 = pdi[i].doubleValue() + mdi[i].doubleValue();
			if (v1 == 0 || v2 == 0)
				dx[i] = 0;
			else
				dx[i] = v1 / v2;
		}

		// ADX を求めます。
		final Number[] adx = sma(dx, period_adx);
		for (int i = 0; i < length; i++) {
			if (adx[i] == null)
				continue;
			adx[i] = adx[i].doubleValue() * unit.scale();
		}

		// ADXR を求めます。
		final Number[] adxr = new Number[length];
		for (int i = period_adx -1; i < length; i++) {
			final int j = i - period_adx + 1;
			if (adx[i] == null || adx[j] == null)
				continue;
			adxr[i] = (adx[i].doubleValue() + adx[j].doubleValue()) * 0.5;
		}

		final Map<DMI, Number[]> map = new EnumMap<DMI, Number[]>(DMI.class);
		map.put(DMI.PLUS_DI, pdi);
		map.put(DMI.MINUS_DI, mdi);
		map.put(DMI.ADX, adx);
		map.put(DMI.ADX_R, adxr);
		return map;
	}

	/**
	 * <p>指定されたデータから動的モメンタム指数 (Chande's Dynamic Momentum Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #dmi(Number[], int, int, MovingAverage, int, double, double, PercentageScale)} を
	 * 標準偏差の期間 <code>5</code>、移動平均の期間 <code>10</code>、移動平均の種類 {@link MovingAverage#SMA}、
	 * 期間 <code>14</code>、上限 <code>30</code>、下限 <code>3</code>、
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @return 動的モメンタム指数 (Chande's Dynamic Momentum Index)
	 * @see TechnicalAnalysis#dmi(Number[], PercentageScale)
	 */
	@Deprecated
	public static Number[] dmi(final Number[] x) {
		return dmi(x, 5, 10, MovingAverage.SMA, 14, 30, 3, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから動的モメンタム指数 (Chande's Dynamic Momentum Index) を返します。
	 * 
	 * @param x データ
	 * @param period_stddev 標準偏差の期間
	 * @param period_ma 移動平均の期間
	 * @param matype 移動平均の種類
	 * @param period_dmi 期間
	 * @param upper 上限
	 * @param lower 下限
	 * @param unit 百分率の尺度
	 * @return 動的モメンタム指数 (Chande's Dynamic Momentum Index)
	 */
	@Deprecated
	public static Number[] dmi(final Number[] x, final int period_stddev, final int period_ma, final MovingAverage matype, final int period_dmi, final double upper, final double lower, final PercentageScale unit) {
		// http://www.fmlabs.com/reference/default.htm?url=DMI.htm

		// VIDYAと同様にボラティリティによって算出期間を変える指標です。VIDYAが期間の変化する移動平均だったのに対し、VLDMIは算出期間が変化するRSIです。
		// 算出期間を変えるための方法はVIDYAと同様

		// VI = 5日間の終値の標準偏差　／　5日間の終値の標準偏差の10日平均
		// VL= 基準とする日数(14)／VI   (VLは５～３０の整数になる)
		// VLDMI = VL日間のRSI

		// = 100*(VL日間の上昇幅合計/VL日間の変化幅合計)
		// VLDMIの読み方はRSIと同様70～80で買われすぎ、20～30で売られすぎと見ます。
		// RSIに対するVLDMIの優位性はVLDMIのほうがRSIに対しやや先行するということです。RSIより先にボトムアウトないしピークアウトするのでRSIを用いるときより機動的なポジションメークができます。
		// http://www.tradesignalonline.com/Lexicon/Default.aspx?name=Dynamic+Momentum+Index+(DYMOI)
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		final Number[] sd = stddevp(x, period_stddev);	// 5 period standard deviation
		final Number[] asd = ma(sd, period_ma, matype);	// 10 period average of sd

		for (int i = 10 - 1; i < length; i++) {
			if (sd[i] == null || asd[i] == null || x[i] == null)
				continue;

			// variable length (3...30)
			final int vl = (int) Math.max(Math.min(Math.floor(period_dmi / (sd[i].doubleValue() / asd[i].doubleValue())), upper), lower);
			final int start = i - vl + 1;
			if (start >= 0) {
				double up = 0;		// 値上り合計
				double down = 0;	// 値下り合計

				for (int j = start; j <= i; j++) {
					if (x[j] == null)
						continue;

					final double change = x[j].doubleValue();
					if (change > 0)
						up = up + change;
					else if (change < 0)
						down = down - change;
				}

				final double updown = up + down;
				if (updown <= 0)
					results[i] = unit.scale() * 0.5;
				else
					results[i] = up / updown * unit.scale();
			}
		}
		return results;
	}

	/**
	 * <p>指定されたデータから DPO (Detrended Price Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #dpo(Number[], int, MovingAverage)} を移動平均の種類 {@link MovingAverage#SMA} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return DPO (Detrended Price Oscillator)
	 * @see TechnicalAnalysis#dpo(Number[], int, MovingAverage)
	 */
	public static Number[] dpo(final Number[] x, final int period) {
		return dpo(x, period, MovingAverage.SMA);
	}

	/**
	 * 指定されたデータから DPO (Detrended Price Oscillator) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return DPO (Detrended Price Oscillator)
	 */
	public static Number[] dpo(final Number[] x, final int period, final MovingAverage matype) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		final Number[] ma = ma(x, period, matype);
		final int n = period / 2 + 1;

		for (int i = n; i < length; i++) {
			final int j = i - n;
			if (x[i] == null || ma[j] == null)
				continue;
			results[i] = x[i].doubleValue() - ma[j].doubleValue();
		}
		return results;
	}

	/**
	 * 指定されたデータから EMV (Ease of Movement) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param volume 出来高
	 * @param period 期間
	 * @return EMV (Ease of Movement)
	 */
	// Richard W. Arms, Jr
	public static Number[] emv(final Number[] high, final Number[] low, final Number[] volume, final int period) {
		final int length = getMinLength(high, low, volume);
		final Number[] results = new Number[length];
		for (int i = period; i < length; i++) {
			final int j = i - period;
			if (high[i] == null || low[i] == null || high[j] == null || low[j] == null || volume[i] == null)
				continue;
			final double h = high[i].doubleValue();
			final double l = low[i].doubleValue();
			final double h2 = high[j].doubleValue();
			final double l2 = low[j].doubleValue();
			final double v = volume[i].doubleValue();
			results[i] = ((h + l) * 0.5 - (h2 + l2) * 0.5) / (v / (h - l));
		}
		return results;
	}

	/**
	 * <p>指定されたデータから乖離率を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #kairi(Number[], Number[], PercentageScale)} を百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param ma 移動平均
	 * @return 乖離率
	 * @see TechnicalAnalysis#kairi(Number[], Number[], PercentageScale)
	 */
	public static Number[] kairi(final Number[] x, final Number[] ma) {
		return kairi(x, ma, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから乖離率を返します。
	 * 
	 * @param x データ
	 * @param ma 移動平均
	 * @param unit 百分率の尺度
	 * @return 乖離率
	 */
	public static Number[] kairi(final Number[] x, final Number[] ma, final PercentageScale unit) {
		final int length = getMinLength(x, ma);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (x[i] == null || ma[i] == null)
				continue;
			final double diff = x[i].doubleValue() - ma[i].doubleValue();
			if (diff == 0.0 || ma[i].doubleValue() == 0.0)
				results[i] = 0.0;
			else
				results[i] = diff / ma[i].doubleValue() * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから移動平均収束拡散法 (Moving Average Convergence/Divergence) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #macd(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)} を
	 * 短期期間の移動平均の種類 {@link MovingAverage#EMA}、
	 * 長期期間の移動平均の種類 {@link MovingAverage#EMA}、
	 * シグナルの移動平均の種類 {@link MovingAverage#SMA} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param period シグナルの期間
	 * @return 移動平均収束拡散法 (Moving Average Convergence/Divergence)
	 * @see TechnicalAnalysis#macd(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)
	 */
	public static Map<Histogram, Number[]> macd(final Number[] x, final int fast, final int slow, final int period) {
		return macd(x, fast, MovingAverage.EMA, slow, MovingAverage.EMA, period, MovingAverage.SMA);
	}

	/**
	 * <p>指定されたデータから移動平均収束拡散法 (Moving Average Convergence/Divergence) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #macd(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)} を
	 * 短期期間の移動平均の種類 {@link MovingAverage#EMA}、
	 * 長期期間の移動平均の種類 {@link MovingAverage#EMA} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param period シグナルの期間
	 * @param matype 移動平均の種類
	 * @return 移動平均収束拡散法 (Moving Average Convergence/Divergence)
	 * @see TechnicalAnalysis#macd(Number[], int, MovingAverage, int, MovingAverage, int, MovingAverage)
	 */
	public static Map<Histogram, Number[]> macd(
			final Number[] x, final int fast, final int slow,
			final int period, final MovingAverage matype)
	{
		return macd(x, fast, MovingAverage.EMA, slow, MovingAverage.EMA, period, matype);
	}

	/**
	 * <p>指定されたデータから移動平均収束拡散法 (Moving Average Convergence/Divergence) を返します。</p>
	 * <p>
	 * MACD はジェラルド アペル (Gerald Appel) 氏によって開発されたテクニカル指標で、
	 * 2 本の指数平滑移動平均線を用いて方向性や乖離、絡み具合に注目します。
	 * </p>
	 * <pre>
	 * MACD＝短期移動平均－長期移動平均(EMAが多く用いられる)
	 * Signal＝MACDの移動平均(EMAまたはSMAが多く用いられる)
	 * </pre>
	 * 
	 * @param x データ
	 * @param fast 短期期間
	 * @param matype_fast 短期期間の移動平均の種類
	 * @param slow 長期期間
	 * @param matype_slow 長期期間の移動平均の種類
	 * @param period シグナルの期間
	 * @param matype_signal シグナルの移動平均の種類
	 * @return 移動平均収束拡散法 (Moving Average Convergence/Divergence)
	 */
	public static Map<Histogram, Number[]> macd(
			final Number[] x,
			final int fast, final MovingAverage matype_fast,
			final int slow, final MovingAverage matype_slow,
			final int period, final MovingAverage matype_signal)
	{
		final Number[] macd      = sub(ma(x, fast, matype_fast), ma(x, slow, matype_slow));
		final Number[] signal    = ma(macd, period, matype_signal);
		final Number[] histogram = sub(macd, signal);
		final Map<Histogram, Number[]> map = new EnumMap<Histogram, Number[]>(Histogram.class);
		map.put(Histogram.INDICATOR, macd);
		map.put(Histogram.SIGNAL, signal);
		map.put(Histogram.HISTOGRAM, histogram);
		return map;
	}

	/**
	 * 指定されたデータから MFI (Money Flow Index) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @param period 期間
	 * @return MFI (Money Flow Index)
	 */
	public static Number[] mfi(final Number[] high, final Number[] low, final Number[] close, final Number[] volume, final int period) {
		final int length = getMinLength(high, low, close, volume);
		final Number[] results = new Number[length];

		final Number[] tp = avg(high, low, close);	// Typical Price
		final Number[] mf = mult(tp, volume);		// Money Flow

		final Number[] pmf = new Number[length];		// Positive Money Flow
		final Number[] nmf = new Number[length];		// Negative Money Flow
		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (tp[i] == null || tp[j] == null)
				continue;

			final double current_tp = tp[i].doubleValue();
			final double prev_tp = tp[j].doubleValue();

			if (current_tp > prev_tp) {
				pmf[i] = mf[i].doubleValue();
				nmf[i] = 0;
			} else if (current_tp < prev_tp) {
				pmf[i] = 0;
				nmf[i] = mf[i].doubleValue();
			}
		}

		final Number[] mr = div(sum(pmf, period), sum(nmf, period));	// Money Ratio
		for (int i = period - 1; i < length; i++) {
			if (mr[i] == null)
				continue;
			results[i] = 100.0 - 100.0 / (1.0 + mr[i].doubleValue());
		}
		return results;
	}

	/**
	 * <p>指定されたデータからマス インデックス (Mass Index)を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #mi(Number[], Number[], int, int, MovingAverage)} を移動平均の期間 <code>9</code>、
	 * 移動平均の種類 {@link MovingAverage#EMA} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period 集計期間
	 * @return マス インデックス (Mass Index)
	 * @see TechnicalAnalysis#mi(Number[], Number[], int, int, MovingAverage)
	 */
	// Donald Dorsey
	public static Number[] mi(final Number[] high, final Number[] low, final int period) {
		return mi(high, low, period, 9, MovingAverage.EMA);
	}

	/**
	 * 指定されたデータからマス インデックス (Mass Index)を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period 集計期間
	 * @param period_ma 移動平均の期間
	 * @param matype 移動平均の種類
	 * @return マス インデックス (Mass Index)
	 */
	// Donald Dorsey
	public static Number[] mi(
			final Number[] high, final Number[] low, final int period,
			final int period_ma, final MovingAverage matype)
	{
		final Number[] ma = ma(sub(high, low), period_ma, matype);
		return sum(div(ma, ma(ma, period_ma, matype)), period);
	}

	/**
	 * 指定されたデータからモメンタム (Momentum) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return モメンタム (Momentum)
	 */
	public static Number[] mom(final Number[] x, final int period) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period; i < length; i++) {
			final int j = i - period;
			if (x[i] == null || x[j] == null)
				continue;
			results[i] = x[i].doubleValue() - x[j].doubleValue();
		}
		return results;
	}

	/**
	 * 指定されたデータから PAIN (Price Action Indicator) を返します。
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return PAIN (Price Action Indicator)
	 */
	public static Number[] pain(final Number[] open, final Number[] high, final Number[] low, final Number[] close) {
		final int length = getMinLength(open, high, low, close);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (open[i] == null || high[i] == null || low[i] == null || close[i] == null)
				continue;
			final double c = close[i].doubleValue();
			results[i] = ((c - open[i].doubleValue()) + (c - high[i].doubleValue()) + (c - low[i].doubleValue())) * 0.5; 
		}
		return results;
	}

	/**
	 * <p>指定されたデータから前日比(率) (Percent Change) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #roc(Number[], int, PercentageScale)} を期間 <code>1</code>、
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @return 前日比(率) (Percent Change)
	 * @see TechnicalAnalysis#roc(Number[], int, PercentageScale)
	 */
	public static Number[] pchg(final Number[] x) {
		return roc(x, 1, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから前日比(率) (Percent Change) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #roc(Number[], int, PercentageScale)} を期間 <code>1</code> で呼出すだけです。
	 * 
	 * @param x データ
	 * @param unit 百分率の尺度
	 * @return 前日比(率) (Percent Change)
	 * @see TechnicalAnalysis#roc(Number[], int, PercentageScale)
	 */
	public static Number[] pchg(final Number[] x, final PercentageScale unit) {
		return roc(x, 1, unit);
	}

	/**
	 * <p>指定されたデータからパーセント レジスタンス (Percent of Resistance) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #pcr(Number[], int, PercentageScale)} を百分率の尺度 {@link PercentageScale#REVERSE_PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return パーセント レジスタンス (Percent of Resistance)
	 * @see TechnicalAnalysis#pcr(Number[], int, PercentageScale)
	 */
	public static Number[] pcr(final Number[] x, final int period) {
		return pcr(x, period, PercentageScale.REVERSE_PERCENT);
	}

	/**
	 * <p>指定されたデータからパーセント レジスタンス (Percent of Resistance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return パーセント レジスタンス (Percent of Resistance)
	 */
	public static Number[] pcr(final Number[] x, final int period, final PercentageScale unit) {
		final Number[] highest = highest(x, period);
		final Number[] lowest = lowest(x, period);
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (x[i] == null || highest[i] == null || lowest[i] == null)
				continue;
			final double num = highest[i].doubleValue() - x[i].doubleValue();
			final double den = highest[i].doubleValue() - lowest[i].doubleValue();
			if (num == 0.0 || den == 0.0)
				results[i] = 0.0;
			else
				results[i] = num / den * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから騰落価格 (Performance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 騰落価格 (Performance)
	 */
	public static Number[] performance(final Number[] x, final int period) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		final int start = indexOfNotNull(x, period);
		if (start == -1)
			return results;

		final double prev = x[start].doubleValue();
		for (int i = start + 1; i < length; i++) {
			// 後部拡張は無視します。
			if (x[i] == null)
				break;
			results[i] = x[i].doubleValue() - prev;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから騰落率 (Performance) を返します。</p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 騰落率 (Performance)
	 */
	public static Number[] performance(final Number[] x, final int period, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		final int start = indexOfNotNull(x, period);
		if (start == -1)
			return results;

		final double prev = x[start].doubleValue();
		for (int i = start + 1; i < length; i++) {
			// 後部拡張は無視します。
			if (x[i] == null)
				break;
			results[i] = (x[i].doubleValue() - prev) / prev * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから価格オシレーター (Percentage Price Oscillator)を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #ppo(Number[], int, int, MovingAverage, PercentageScale)} を
	 * 移動平均の種類 {@link MovingAverage#EMA}、百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param prices 価格
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @return 価格オシレーター (Percentage Price Oscillator)
	 * @see TechnicalAnalysis#ppo(Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Number[] ppo(final Number[] prices, final int fast, final int slow) {
		return ppo(prices, fast, slow, MovingAverage.EMA, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから価格オシレーター (Percentage Price Oscillator)を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #ppo(Number[], int, int, MovingAverage, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param prices 価格
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @return 価格オシレーター (Percentage Price Oscillator)
	 * @see TechnicalAnalysis#ppo(Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Number[] ppo(final Number[] prices, final int fast, final int slow, final MovingAverage matype) {
		return ppo(prices, fast, slow, matype, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから価格オシレーター (Percentage Price Oscillator)を返します。
	 * 
	 * @param prices 価格
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @param unit 百分率の尺度
	 * @return 価格オシレーター (Percentage Price Oscillator)
	 */
	public static Number[] ppo(final Number[] prices, final int fast, final int slow, final MovingAverage matype, final PercentageScale unit) {
		final Number[] ma_fast = ma(prices, Math.min(fast, slow), matype);
		final Number[] ma_slow = ma(prices, Math.max(fast, slow), matype);

		final int length = getMinLength(prices);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (ma_fast[i] == null || ma_slow[i] == null)
				continue;
			results[i] = (ma_fast[i].doubleValue() - ma_slow[i].doubleValue()) / ma_fast[i].doubleValue() * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータからサイコロジカル ライン (Psychological Line) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #psy(Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比
	 * @param period 期間
	 * @return サイコロジカル ライン (Psychological Line)
	 * @see TechnicalAnalysis#psy(Number[], int, PercentageScale)
	 */
	public static Number[] psy(final Number[] change, final int period) {
		return psy(change, period, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータからサイコロジカル ライン (Psychological Line) を返します。</p>
	 * <p>
	 * サイコロジカルラインは、オシレーター系指標で、市場心理を測るテクニカルです。
	 * </p>
	 * <pre>
	 * サイコロ＝過去n日間の前日比プラス日数÷n×100
	 * </pre>
	 * 
	 * @param change 前日比
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return サイコロジカル ライン (Psychological Line)
	 */
	public static Number[] psy(final Number[] change, final int period, final PercentageScale unit) {
		final int length = getMinLength(change);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;
			int upCount = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null)
					continue;
				final double chg = change[j].doubleValue();
				if (chg > 0)
					upCount = upCount + 1;
			}
			results[i] = (double) upCount / (double) period * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータからQスティック インジゲーター (Chande's Qstick Indicator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #qstick(Number[], Number[], int, MovingAverage)} を
	 * 移動平均の種類 {@link MovingAverage#SMA} で呼出すだけです。
	 * 
	 * @param open 始値
	 * @param close 終値
	 * @param period 期間
	 * @return Qスティック インジゲーター (Chande's Qstick Indicator)
	 * @see TechnicalAnalysis#qstick(Number[], Number[], int, MovingAverage)
	 */
	public static Number[] qstick(final Number[] open, final Number[] close, final int period) {
		return qstick(open, close, period, MovingAverage.SMA);
	}

	/**
	 * 指定されたデータからQスティック インジゲーター (Chande's Qstick Indicator) を返します。
	 * 
	 * @param open 始値
	 * @param close 終値
	 * @param period 期間
	 * @param matype 移動平均の種類
	 * @return Qスティック インジゲーター (Chande's Qstick Indicator)
	 */
	public static Number[] qstick(final Number[] open, final Number[] close, final int period, final MovingAverage matype) {
		return ma(sub(close, open), period, matype);
	}

	/**
	 * <p>指定されたデータから順位相関係数 (Rank Correlation Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #rci(Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 順位相関係数 (Rank Correlation Index)
	 * @see TechnicalAnalysis#rci(Number[], int, PercentageScale)
	 */
	public static Number[] rci(final Number[] x, final int period) {
		return rci(x, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから順位相関係数 (Rank Correlation Index) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 順位相関係数 (Rank Correlation Index)
	 */
	public static Number[] rci(final Number[] x, final int period, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		double table[] = new double[period];
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null)
				continue;

			double d = 0;
			// table を 1 で初期化します
			for (int j = 0; j < period; j++)
				table[j] = 1;

			for (int j = 0; j < period; j++) {
				for (int k = j + 1; k < period; k++) {
					Number n1 = x[(i - period) + 1 + j];
					Number n2 = x[(i - period) + 1 + k];
					if (n1 == null || n2 == null)
						continue;
					if (n1.doubleValue() < n2.doubleValue()) {
						table[k] = table[k] + 1;
					} else if (n1.doubleValue() > n2.doubleValue()) {
						table[j] = table[j] + 1;
					} else {
						table[j] = table[j] + 0.5;
						table[k] = table[k] + 0.5;
					}
				}
				d = d + Math.pow((j + 1) - table[j], 2);
			}

			results[i] = (1 - (6 * d) / (period * (period * period - 1))) * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから変化率 (Rate of Change) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #roc(Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return 変化率 (Rate of Change)
	 * @see TechnicalAnalysis#roc(Number[], int, PercentageScale)
	 */
	public static Number[] roc(final Number[] x, final int period) {
		return roc(x, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから変化率 (Rate of Change) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 変化率 (Rate of Change)
	 */
	public static Number[] roc(final Number[] x, final int period, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];
		for (int i = period; i < length; i++) {
			final int j = i - period;
			if (x[i] == null || x[j] == null)
				continue;
			final double diff = x[i].doubleValue() - x[j].doubleValue();
			if (diff == 0.0 || x[j].doubleValue() == 0.0)
				results[i] = 0.0;
			else
				results[i] = diff / x[j].doubleValue() * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから相対力指数 (Relative Strength Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #rsi(Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @return 相対力指数 (Relative Strength Index)
	 * @see TechnicalAnalysis#rsi(Number[], int, PercentageScale)
	 */
	// Welles Wider
	public static Number[] rsi(final Number[] change, final int period) {
		return rsi(change, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから相対力指数 (Relative Strength Index) を返します。
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 相対力指数 (Relative Strength Index)
	 */
	// Welles Wider
	public static Number[] rsi(final Number[] change, final int period, final PercentageScale unit) {
		final int length = getMinLength(change);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;

			double gain = 0;		// 値上り合計
			double loss = 0;	// 値下り合計
			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null)
					continue;

				final double diff = change[j].doubleValue();
				if (diff > 0)
					gain = gain + diff;
				else if (diff < 0)
					loss = loss - diff;
			}

//			results[i] = 100 - 100  / (1 + Math.abs(up) / Math.abs(down));
			final double updown = gain + loss;
			if (updown <= 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = gain / updown * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから RVI (Relative Volatility Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は、百分率の尺度を {@link PercentageScale#PERCENT} で {@link #rvi(Number[], int, PercentageScale)} を呼出すだけです。
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @return RVI (Relative Volatility Index)
	 * @see TechnicalAnalysis#rvi(Number[], int, PercentageScale)
	 */
	// Donald Dorsey
	public static Number[] rvi(final Number[] change, final int period) {
		return rvi(change, period, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから RVI (Relative Volatility Index) を返します。</p>
	 * 
	 * @param change 前日比(幅)
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return RVI (Relative Volatility Index)
	 * @see TechnicalAnalysis#stddev(Number[], int)
	 * @see TechnicalAnalysis#rsi(Number[], int, PercentageScale)
	 */
	// Donald Dorsey
	public static Number[] rvi(final Number[] change, final int period, final PercentageScale unit) {
		final Number[] stddev = stddev(change, 10);
		final int length = getMinLength(change);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null || stddev[i] == null)
				continue;

			double up = 0;	// 値上り合計
			double down = 0;	// 値下り合計
			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null || stddev[j] == null)
					continue;

				final double chg = change[j].doubleValue();
				if (chg > 0)
					up = up + stddev[j].doubleValue();
				else if (chg < 0)
					down = down + stddev[j].doubleValue();
			}
			if (up != 0.0) up = up / period;
			if (down != 0.0) down = down / period;

			final double updown = up + down;
			if (updown <= 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = up / updown * unit.scale();
		}
		return results;
	}

	/**
	 * 指定されたデータから強弱レシオ (篠原レシオ) を返します。
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return 強弱レシオ (篠原レシオ)
	 */
	public static Map<Shinohara, Number[]> shinohara(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final int period) {
		return shinohara(open, high, low, close, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから強弱レシオ (篠原レシオ) を返します。
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 強弱レシオ (篠原レシオ)
	 */
	public static Map<Shinohara, Number[]> shinohara(final Number[] open, final Number[] high, final Number[] low, final Number[] close, final int period, final PercentageScale unit) {
		final int length = getMinLength(open, high, low, close);
		final Number[] a = new Number[length];
		final Number[] b = new Number[length];

		for (int i = period; i < length; i++) {
			if (close[i] == null)
				continue;

			double bull_energy = 0;	// 強エネルギー
			double bear_energy = 0;	// 弱エネルギー
			double bull_active = 0;	// 強人気
			double bear_active = 0;	// 弱人気

			for (int j = i - period + 1; j <= i; j++) {
				if (open[j] == null || high[j] == null || low[j] == null || close[j - 1] == null)
					continue;

				final double o = open[j].doubleValue();
				final double h = high[j].doubleValue();
				final double l = low[j].doubleValue();
				final double prev_c = close[j - 1].doubleValue();
				bull_energy = bull_energy + (h - o);
				bear_energy = bear_energy + (o - l);
				bull_active = bull_active + (h - prev_c);
				bear_active = bear_active + (prev_c - l);
			}

			if (bear_energy > 0)
				a[i] = bull_energy / bear_energy * unit.scale();
			else
				a[i] = 1000;

			if (bear_active > 0)
				b[i] = bull_active / bear_active * unit.scale();
			else
				b[i] = 1000;
		}

		final Map<Shinohara, Number[]> map = new EnumMap<Shinohara, Number[]>(Shinohara.class);
		map.put(Shinohara.A_RATIO, a);
		map.put(Shinohara.B_RATIO, b);
		map.put(Shinohara.C_RATIO, cratio(high, low, period, unit));
		map.put(Shinohara.MAIN_BAND1, displace(cratio(high, low, 40, unit), 17 - 1));
		map.put(Shinohara.MAIN_BAND2, displace(cratio(high, low, 52, unit), 26 - 1));
		map.put(Shinohara.SUB_BAND1, displace(cratio(high, low, 10, unit), 5 - 1));
		map.put(Shinohara.SUB_BAND2, displace(cratio(high, low, 20, unit), 9 - 1));
		return map;
	}

	/**
	 * <p>指定されたデータから篠原Cレシオ(篠原レシオの C レシオ)を返します。</p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 篠原Cレシオ(篠原レシオの C レシオ)
	 */
	protected static Number[] cratio(final Number[] high, final Number[] low, final int period, final PercentageScale unit) {
		final int length = getMinLength(high, low);
		final Number[] results = new Number[length];
		for (int i = period; i < length; i++) {
			if (high[i] == null || low[i] == null)
				continue;
			double plus_energy = 0;	// +エネルギー
			double minus_energy = 0;	// -エネルギー
			for (int j = i - period + 1; j <= i; j++) {
				if (high[j] == null || low[j] == null || high[j - 1] == null || low[j - 1] == null)
					continue;

				final double prev_mid = (high[j - 1].doubleValue() + low[j - 1].doubleValue()) * 0.5;
				plus_energy = plus_energy + (high[j].doubleValue() - prev_mid);
				minus_energy = minus_energy + (prev_mid - low[j].doubleValue());
			}
			results[i] = plus_energy / minus_energy * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータからストキャスティクス (Stochastics) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #srv(Number[], Number[], Number[], int, int, MovingAverage, PercentageScale)} を
	 * 移動平均の種類 {@link MovingAverage#SMA}、百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_k %K の期間
	 * @param period_d %D の期間
	 * @return ストキャスティクス (Stochastics)
	 * @see TechnicalAnalysis#srv(Number[], Number[], Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Map<Stochastics, Number[]> srv(final Number[] high, final Number[] low, final Number[] close, final int period_k, final int period_d) {
		return srv(high, low, close, period_k, period_d, MovingAverage.SMA, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータからストキャスティクス (Stochastics) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #srv(Number[], Number[], Number[], int, int, MovingAverage, PercentageScale)} を
	 * 移動平均の種類 {@link MovingAverage#SMA} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_k %K の期間
	 * @param period_d %D の期間
	 * @param unit 百分率の尺度
	 * @return ストキャスティクス (Stochastics)
	 * @see TechnicalAnalysis#srv(Number[], Number[], Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Map<Stochastics, Number[]> srv(final Number[] high, final Number[] low, final Number[] close, final int period_k, final int period_d, final PercentageScale unit) {
		return srv(high, low, close, period_k, period_d, MovingAverage.SMA, unit);
	}

	/**
	 * <p>指定されたデータからストキャスティクス (Stochastics) を返します。</p>
	 * <p>
	 * ストキャスティクスはジョージ レーンによって開発されたオシレーター系のテクニカル指標で、
	 * ある一定期間の高値と安値の値幅に対する当日の終値の相対的な位置によって評価するものです。
	 * %Kラインと%Dライン(または%DラインとS%Dライン)という2本の線を併用するところに特徴があります。
	 * </p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period_k %K の期間
	 * @param period_d %D の期間
	 * @param matype %SD の期間
	 * @param unit 百分率の尺度
	 * @return ストキャスティクス (Stochastics)
	 */
	// 1950年代 ジョージ・レーン
	public static Map<Stochastics, Number[]> srv(final Number[] high, final Number[] low, final Number[] close, final int period_k, final int period_d, final MovingAverage matype, final PercentageScale unit) {
		final Number[] highest = TechnicalAnalysis.highest(high, period_k);
		final Number[] lowest = TechnicalAnalysis.lowest(low, period_k);

		final int length = getMinLength(high, low, close);
		final Number[] k = new Number[length];
		final Number[] d = new Number[length];
		for (int i = period_k; i < length; i++) {
			if (close[i] == null || lowest[i] == null || highest[i] == null)
				continue;

			if (highest[i].doubleValue() > lowest[i].doubleValue())
				k[i] = ((close[i].doubleValue() - lowest[i].doubleValue()) / (highest[i].doubleValue() - lowest[i].doubleValue())) * 100;

			double h3 = 0;
			double l3 = 0;

			for (int j = i - period_d + 1; j <= i; j++) {
				if (close[j] == null || lowest[j] == null || highest[j] == null)
					continue;

				h3 = h3 + (close[j].doubleValue() - lowest[j].doubleValue());
				l3 = l3 + (highest[j].doubleValue() - lowest[j].doubleValue());
			}

			if (l3 <= 0)
				d[i] = unit.scale() * 0.5;
			else
				d[i] = (h3 / l3) * unit.scale();
		}

		final Map<Stochastics, Number[]> map = new EnumMap<Stochastics, Number[]>(Stochastics.class);
		map.put(Stochastics.K, k);
		map.put(Stochastics.D, d);
		map.put(Stochastics.SD, ma(d, period_k, matype));// FIXME - %Kの期間ではなくslow%D
		return map;
	}

	/**
	 * 指定されたデータからストキャスティクス RSI (Stochastics RSI) を返します。
	 * 
	 * @param x データ
	 * @param period_rsi RSI の期間
	 * @param period_k %K の期間
	 * @param period_d %D の期間
	 * @param matype %SD の移動平均の種類
	 * @param unit 百分率の尺度
	 * @return ストキャスティクス RSI (Stochastics RSI)
	 */
	public static Map<Stochastics, Number[]> srvrsi(final Number[] x, final int period_rsi, final int period_k, final int period_d, final MovingAverage matype, final PercentageScale unit) {
		final Number[] rsi = rsi(x, period_rsi, unit);
		return srv(rsi, rsi, rsi, period_k, period_d, matype, unit);
	}

	/**
	 * <p>指定されたデータから貿易強度指数 (Trend Intensity Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #tii(Number[], int, MovingAverage, PercentageScale)} を
	 * 移動平均の種類 {@link MovingAverage#SMA}、百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param 期間
	 * @return 貿易強度指数 (Trend Intensity Index)
	 * @see TechnicalAnalysis#tii(Number[], int, MovingAverage)
	 */
	public static Number[] tii(final Number[] x, final int period) {
		return tii(x, period, MovingAverage.SMA, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから貿易強度指数 (Trend Intensity Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #tii(Number[], int, MovingAverage, PercentageScale)} を
	 *百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param x データ
	 * @param 期間
	 * @param matype 移動平均の種類
	 * @return 貿易強度指数 (Trend Intensity Index)
	 * @see TechnicalAnalysis#tii(Number[], int, MovingAverage)
	 */
	public static Number[] tii(final Number[] x, final int period, final MovingAverage matype) {
		return tii(x, period, matype, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから貿易強度指数 (Trend Intensity Index) を返します。
	 * 
	 * @param x データ
	 * @param 期間
	 * @param matype 移動平均の種類
	 * @param unit 百分率の尺度
	 * @return 貿易強度指数 (Trend Intensity Index)
	 */
	public static Number[] tii(final Number[] x, final int period, final MovingAverage matype, final PercentageScale unit) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		final Number[] sub = sub(x, ma(x, period, matype));

		for (int i = period - 1; i < length; i++) {
			if (sub[i] == null)
				continue;

			double sdpos = 0;	// 値上り合計
			double sdneg = 0;	// 値下り合計

			for (int j = i - period + 1; j <= i; j++) {
				if (sub[j] == null)
					continue;

				final double diff = sub[j].doubleValue();
				if (diff > 0)
					sdpos = sdpos + diff;
				else
					sdneg = sdneg + Math.abs(diff);
			}

			final double sum = sdpos + sdneg;
			if (sum == 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = sdpos / (sdpos + sdneg) * unit.scale();
		}

		return results;
	}

	/**
	 * <p>指定されたデータからトリックス (TRIX) を返します。</p>
	 * <p>
	 * トリックスはジャック ヒューストン (Jack Hutson) 氏によって考案されたモメンタム系のオシレーター指標で、MACDによく似た計算方法と捉え方をします。
	 * 指数平滑移動平均(EMA)により3回スムージングをするため、トリプルエクスポーネンシャル≠トリックスと呼ばれています。
	 * </p>
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param period_signal シグナルの期間
	 * @param matype_signal シグナルの移動平均の種類
	 * @return トリックス (TRIX)
	 */
	public static Map<Histogram, Number[]> trix(final Number[] x, final int period, final int period_signal, final MovingAverage matype_signal) {
		final Number[] trix = roc(tripleSmooth(x, period, MovingAverage.EMA), 1, PercentageScale.PERCENT);
		final Number[] signal = ma(trix, period_signal, matype_signal);
		final Number[] histogram = sub(trix, signal);
		final Map<Histogram, Number[]> map = new EnumMap<Histogram, Number[]>(Histogram.class);
		map.put(Histogram.INDICATOR, trix);
		map.put(Histogram.SIGNAL, signal);
		map.put(Histogram.HISTOGRAM, histogram);
		return map;
	}

	/**
	 * 指定されたデータから真力指数 (True Strength Index) を返します。
	 * 
	 * @param change 前日比
	 * @param period1 期間1
	 * @param period2 期間2
	 * @param period_signal シグナルの期間
	 * @param matype_signal シグナルの移動平均の種類
	 * @return 真力指数 (True Strength Index)
	 */
	public static Map<Histogram, Number[]> tsi(final Number[] change,
			final int period1, final int period2,
			final int period_signal, final MovingAverage matype_signal)
	{
		return tsi(change, period1, MovingAverage.EMA, period2, MovingAverage.EMA, period_signal, matype_signal, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから真力指数 (True Strength Index) を返します。
	 * 
	 * @param change 前日比
	 * @param period1 期間1
	 * @param period2 期間2
	 * @param matype2 期間1と2の移動平均の種類
	 * @param period_signal シグナルの期間
	 * @param matype_signal シグナルの移動平均の種類
	 * @return 真力指数 (True Strength Index)
	 */
	public static Map<Histogram, Number[]> tsi(
			final Number[] change,
			final int period1, final int period2, final MovingAverage matype,
			final int period_signal, final MovingAverage matype_signal)
	{
		return tsi(change, period1, matype, period2, matype, period_signal, matype_signal, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから真力指数 (True Strength Index) を返します。
	 * 
	 * @param change 前日比
	 * @param period1 期間1
	 * @param matype1 期間1の移動平均の種類
	 * @param period2 期間2
	 * @param matype2 期間2の移動平均の種類
	 * @param period_signal シグナルの期間
	 * @param matype_signal シグナルの移動平均の種類
	 * @param unit 百分率の尺度
	 * @return 真力指数 (True Strength Index)
	 */
	// by William Blau
	public static Map<Histogram, Number[]> tsi(final Number[] change,
			final int period1, final MovingAverage matype1,
			final int period2, final MovingAverage matype2,
			final int period_signal, final MovingAverage matype_signal,
			final PercentageScale unit)
	{
		final Number[] chg = doubleSmooth(change, period1, matype1, period2, matype2);
		final Number[] abs = doubleSmooth(abs(change), period1, matype1, period2, matype2);
		final int length = getMinLength(chg, abs);
		final Number[] tsi = new Number[length];//div(doubleSmooth(change, period1, matype1, period2, matype2), doubleSmooth(abs(change), period1, matype1, period2, matype2));
		for (int i = 0; i < length; i++) {
			if (chg[i] == null || abs[i] == null)
				continue;
			if (chg[i].doubleValue() == 0.0 || abs[i].doubleValue() == 0.0)
				tsi[i] = 0.0;
			else
				tsi[i] = chg[i].doubleValue() / abs[i].doubleValue() * unit.scale();
		}
		final Number[] signal    = ma(tsi, period_signal, matype_signal);
		final Number[] histogram = sub(tsi, signal);
		final Map<Histogram, Number[]> map = new EnumMap<Histogram, Number[]>(Histogram.class);
		map.put(Histogram.INDICATOR, tsi);
		map.put(Histogram.SIGNAL, signal);
		map.put(Histogram.HISTOGRAM, histogram);
		return map;
	}

	/**
	 * <p>指定されたデータから究極のオシレーター (Ultimate Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #ultimate(Number[], Number[], Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return 究極のオシレーター (Ultimate Oscillator)
	 * @see TechnicalAnalysis#ultimate(Number[], Number[], Number[], int, PercentageScale)
	 */
	// ラリー・ウィリアムズ
	public static Number[] ultimate(final Number[] high, final Number[] low, final Number[] close, final int period) {
		return ultimate(high, low, close, period, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから究極のオシレーター (Ultimate Oscillator) を返します。</p>
	 * <pre>
	 * BP＝当日終値－当日TL
	 * RawUO＝4×(7日間のBP合計÷7日間のTR合計)＋2×(14日間のBP合計÷14日間のTR合計)＋1×(28日間のBP合計÷28日間のTR合計)
	 * UO＝(RawUO÷(4＋2＋1))×100
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 究極のオシレーター (Ultimate Oscillator)
	 * @see TechnicalAnalysis#tl(Number[], Number[])
	 * @see TechnicalAnalysis#tr(Number[], Number[], Number[])
	 */
	// ラリー・ウィリアムズ
	public static Number[] ultimate(final Number[] high, final Number[] low, final Number[] close, final int period, final PercentageScale unit) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];

		// True Low
		final Number[] tl = tl(low, close);
		// Buying Pressure (買い圧力)
		final Number[] bp = new Number[length];

		// 買い圧力を求めます
		for (int i = 0; i < length; i++) {
			if (close[i] == null || tl[i] == null)
				continue;
			bp[i] = close[i].doubleValue() - tl[i].doubleValue();
		}

		// True Range(値幅)
		final Number[] tr = tr(high, low, close);

		for (int i = (period * 4) - 1; i < length; i++) {
			if (bp[i] == null || tr[i] == null)
				continue;

			double bpSum1 = 0;
			double trSum1 = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (bp[j] == null || tr[j] == null)
					continue;
				bpSum1 = bpSum1 + bp[j].doubleValue();
				trSum1 = trSum1 + tr[j].doubleValue();
			}

			double bpSum2 = 0;
			double trSum2 = 0;
			for (int j = i - (period * 2) + 1; j <= i; j++) {
				if (bp[j] == null || tr[j] == null)
					continue;
				bpSum2 = bpSum2 + bp[j].doubleValue();
				trSum2 = trSum2 + tr[j].doubleValue();
			}

			double bpSum3 = 0;
			double trSum3 = 0;
			for (int j = i - (period * 4) + 1; j <= i; j++) {
				if (bp[j] == null || tr[j] == null)
					continue;
				bpSum3 = bpSum3 + bp[j].doubleValue();
				trSum3 = trSum3 + tr[j].doubleValue();
			}

			final double rawUO = 4 * (bpSum1 / trSum1) + 2 * (bpSum2 / trSum2) + (bpSum3 / trSum3);
			results[i] = (rawUO / (4 + 2 + 1)) * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから VIDYA (Chande's Variable Index Dynamic Average) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #vidya(Number[], int, int)} をボラティリティー (CMO) の期間 <code>9</code> で呼出すだけです。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @return VIDYA (Chande's Variable Index Dynamic Average)
	 * @see TechnicalAnalysis#vidya(Number[], int, int)
	 * @deprecated このメソッドのインタフェースや実装内容は将来的に変更する可能性があります。
	 */
	// 1992 Tushar Chande
	public static Number[] vidya(final Number[] x, final int period) {
		return vidya(x, period, 9);
	}

	/**
	 * 指定されたデータから VIDYA (Chande's Variable Index Dynamic Average) を返します。
	 * 
	 * @param x データ
	 * @param period 期間
	 * @param period_cmo ボラティリティー (CMO) の期間
	 * @return VIDYA (Chande's Variable Index Dynamic Average)
	 * @deprecated このメソッドのインタフェースや実装内容は将来的に変更する可能性があります。
	 */
	// 1992 Tushar Chande
	// smooth = 12 (vidya period)
	// period = 9 (Volitility period)
	public static Number[] vidya(final Number[] x, final int period, final int period_cmo) {
		final int length = getMinLength(x);
		final Number[] results = new Number[length];

		// 平滑化定数 (Smoothing Constant)
		final double sc = 2.0 / (period + 1.0);	// α   0.1～0.9？
		// Volatility Index
		final Number[] vi = abs(cmo(x, period_cmo, PercentageScale.RATE));

		Number vidya = null;
		for (int i = period - 1; i < length; i++) {
			if (x[i] == null || x[i - 1] == null || vi[i] == null)
				continue;

			if (vidya == null) {
				vidya = x[i].doubleValue();
			} else {
				vidya = sc * vi[i].doubleValue() * x[i].doubleValue() + (1 - sc * vi[i].doubleValue()) * x[i - 1].doubleValue();
			}
			results[i] = vidya;
		}
		return results;
	}

	/**
	 * 指定されたデータからウィリアム A/D (William's Accumulation/Distribution) を返します。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return ウィリアム A/D (William's Accumulation/Distribution)
	 * @see TechnicalAnalysis#th(Number[], Number[])
	 * @see TechnicalAnalysis#tl(Number[], Number[])
	 */
	// ラリー・ウイリアムズ
	public static Number[] wad(final Number[] high, final Number[] low, final Number[] close) {
		final Number[] trh = th(high, close);
		final Number[] trl = tl(low, close);

		final int length = getMinLength(close, trh, trl);
		final Number[] ad = new Number[length];
		final Number[] results = new Number[length];

		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (close[i] == null || close[j] == null || trh[i] == null || trl[i] == null)
				continue;

			final double today = close[i].doubleValue();
			if (today > close[j].doubleValue())
				// 本日の終値が昨日の終値より高い場合
				ad[i] = today - trl[i].doubleValue();
			else if (today < close[j].doubleValue())
				// 本日の終値が昨日の終値より低い場合
				ad[i] = today - trh[i].doubleValue();
			else
				// 本日の終値＝昨日の終値の場合
				ad[i] = 0.0;

			// WAD＝今日のAD＋昨日のWAD
			if (results[j] == null)
				results[i] = ad[i].doubleValue();
			else
				results[i] = ad[i].doubleValue() + results[j].doubleValue();
		}
		return results;
	}

	/**
	 * <p>指定されたデータからウィリアム %R (William's %R) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #wr(Number[], Number[], Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#REVERSE_PERCENT} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @return ウィリアム %R (William's %R)
	 * @see TechnicalAnalysis#wr(Number[], Number[], Number[], int, PercentageScale)
	 */
	// Larry Williams
	public static Number[] wr(final Number[] high, final Number[] low, final Number[] close, final int period) {
		return wr(high, low, close, period, PercentageScale.REVERSE_PERCENT);
	}

	/**
	 * <p>指定されたデータからウィリアム %R (William's %R) を返します。</p>
	 * <pre>
	 * %R＝(過去n日間の最高値－当日終値)÷(過去n日間の最高値－過去n日間の最安値)×-100
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return ウィリアム %R (William's %R)
	 * @see TechnicalAnalysis#highest(Number[], int)
	 * @see TechnicalAnalysis#lowest(Number[], int)
	 */
	// Larry Williams
	public static Number[] wr(final Number[] high, final Number[] low, final Number[] close, final int period, final PercentageScale unit) {
		final Number[] highest	= highest(high, period);
		final Number[] lowest	= lowest(low, period);
		final int length = getMinLength(close, highest, lowest);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (highest[i] == null || lowest[i] == null || close[i] == null)
				continue;
			final double d = highest[i].doubleValue() - lowest[i].doubleValue();
			if (d > 0)
				results[i] = (highest[i].doubleValue() - close[i].doubleValue()) / d * unit.scale();
			else
				results[i] = unit.scale() * 0.5;
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 出来高指標 */

	/**
	 * <p>指定されたデータから A/D ライン (Accumulation/Distribution Line) を返します。</p>
	 * <pre>
	 * AD＝CLV×出来高
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @return A/D ライン (Accumulation/Distribution Line)
	 * @see TechnicalAnalysis#clv(Number[], Number[], Number[])
	 */
	public static Number[] ad(final Number[] high, final Number[] low, final Number[] close, final Number[] volume) {
		final Number[] clv = clv(high, low, close);
		final int length = getMinLength(clv, volume);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (clv[i] == null || volume[i] == null)
				continue;
			results[i] = clv[i].doubleValue() * volume[i].doubleValue();
		}
		return results;
	}

	/**
	 * <p>指定されたデータからクローズ ロケーション バリュー (Close Location Value) を返します。</p>
	 * <pre>
	 * CLV＝((終値－安値)－(高値－終値))÷(高値－安値)
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @return クローズ ロケーション バリュー (Close Location Value)
	 */
	public static Number[] clv(final Number[] high, final Number[] low, final Number[] close) {
		final int length = getMinLength(high, low, close);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (high[i] == null || low[i] == null || close[i] == null)
				continue;
			final double h = high[i].doubleValue();
			final double l = low[i].doubleValue();
			final double c = close[i].doubleValue();
			final double clhc = (c - l) - (h - c);
			final double denominator = h - l;
			if (clhc == 0.0 || denominator == 0.0)
				results[i] = 0.0;
			else
				results[i] = clhc / denominator;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからチャイキン オシレーター (Chaikin's Accumulation/Distribution Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #cho(Number[], Number[], Number[], Number[], int, MovingAverage, int, MovingAverage)} を
	 * 短期移動平均の種類 {@link MovingAverage#EMA}、長期移動平均の種類 {@link MovingAverage#EMA} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @param period_fast 短期期間
	 * @param period_slow 長期期間
	 * @return チャイキン オシレーター (Chaikin's Accumulation/Distribution Oscillator)
	 * @see TechnnicalAnalysis#cho(Number[], Number[], Number[], Number[], int, MovingAverage, int, MovingAverage)
	 */
	public static Number[] cho(
			final Number[] high, final Number[] low, final Number[] close, final Number[] volume,
			final int period_fast, final int period_slow)
	{
		return cho(high, low, close, volume, period_fast, MovingAverage.EMA, period_slow, MovingAverage.EMA);
	}

	/**
	 * <p>指定されたデータからチャイキン オシレーター (Chaikin's Accumulation/Distribution Oscillator) を返します。</p>
	 * <pre>
	 * CHO＝ADラインの短期移動平均－ADラインの長期移動平均
	 * </pre>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @param period_fast 短期期間
	 * @param matype_fast 短期移動平均の種類
	 * @param period_slow 長期期間
	 * @param matype_slow 長期移動平均の種類
	 * @return チャイキン オシレーター (Chaikin's Accumulation/Distribution Oscillator)
	 * @see TechnicalAnalysis#ad(Number[], Number[], Number[], Number[])
	 */
	public static Number[] cho(
			final Number[] high, final Number[] low, final Number[] close, final Number[] volume,
			final int period_fast, final MovingAverage matype_fast,
			final int period_slow, final MovingAverage matype_slow)
	{
		final Number[] ad = ad(high, low, close, volume);	// A/Dラインを求めます。
		return sub(ma(ad, period_fast, matype_fast), ma(ad, period_slow, matype_slow));
	}

	/**
	 * <p>指定されたデータから出来高オシレーター (Absolute Volume Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #apo(Number[], int, int)} を呼出すだけです。
	 * 
	 * @param volume 出来高
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @return 出来高オシレーター (Absolute Volume Oscillator)
	 * @see TechnicalAnalysis#apo(Number[], int, int)
	 */
	public static Number[] avo(final Number[] volume, final int fast, final int slow) {
		return apo(volume, fast, slow);
	}

	/**
	 * <p>指定されたデータから出来高オシレーター (Absolute Volume Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #apo(Number[], int, int, MovingAverage)} を呼出すだけです。
	 * 
	 * @param volume 出来高
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @return 出来高オシレーター (Absolute Volume Oscillator)
	 * @see TechnicalAnalysis#avo(Number[], int, int, MovingAverage)
	 */
	public static Number[] avo(final Number[] volume, final int fast, final int slow, final MovingAverage matype) {
		return apo(volume, fast, slow, matype);
	}

	/**
	 * <p>指定されたデータから BW MFI (Market Facilitation Index) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は {@link #bwmfi(Number[], Number[], Number[], PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param volume 出来高
	 * @return BW MFI (Market Facilitation Index)
	 * @see #bwmfi(Number[], Number[], Number[], PercentageScale)
	 */
	public static Number[] bwmfi(final Number[] high, final Number[] low, final Number[] volume) {
		return bwmfi(high, low, volume, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから BW MFI (Market Facilitation Index) を返します。</p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param volume 出来高
	 * @param unit 百分率の尺度
	 * @return BW MFI (Market Facilitation Index)
	 */
	public static Number[] bwmfi(final Number[] high, final Number[] low, final Number[] volume, final PercentageScale unit) {
		final Number[] bwmfi = div(sub(high, low), volume);
		final int length = bwmfi.length;
		for (int i = 0; i < length; i++) {
			if (bwmfi[i] != null)
				bwmfi[i] = bwmfi[i].doubleValue() * unit.scale();
		}
		return bwmfi;
	}

	/**
	 * <p>指定されたデータから出来高オシレーター (Percentage Volume Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #ppo(Number[], int, int)} を呼出すだけです。
	 * 
	 * @param volume 出来高
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @return 出来高オシレーター (Percentage Volume Oscillator)
	 * @see TechnicalAnalysis#ppo(Number[], int, int)
	 */
	public static Number[] pvo(final Number[] volume, final int fast, final int slow) {
		return ppo(volume, fast, slow);
	}

	/**
	 * <p>指定されたデータから出来高オシレーター (Percentage Volume Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #ppo(Number[], int, int, MovingAverage, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param volume 出来高
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @return 出来高オシレーター (Percentage Volume Oscillator)
	 * @see TechnicalAnalysis#ppo(Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Number[] pvo(final Number[] volume, final int fast, final int slow, final MovingAverage matype) {
		return ppo(volume, fast, slow, matype, PercentageScale.PERCENT);
	}

	/**
	 * <p>指定されたデータから出来高オシレーター (Percentage Volume Oscillator) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #ppo(Number[], int, int, MovingAverage, PercentageScale)} を呼出すだけです。
	 * 
	 * @param volume 出来高
	 * @param fast 短期期間
	 * @param slow 長期期間
	 * @param matype 移動平均の種類
	 * @param unit 百分率の尺度
	 * @return 出来高オシレーター (Percentage Volume Oscillator)
	 * @see TechnicalAnalysis#ppo(Number[], int, int, MovingAverage, PercentageScale)
	 */
	public static Number[] pvo(final Number[] volume, final int fast, final int slow, final MovingAverage matype, final PercentageScale unit) {
		return ppo(volume, fast, slow, matype, unit);
	}

	/**
	 * <p>指定されたデータから価格&amp;出来高トレンド (Price and Volume Trend) を返します。</p>
	 * 
	 * @param price 価格
	 * @param volume 出来高
	 * @return 価格&amp;出来高トレンド (Price and Volume Trend)
	 */
	public static Number[] pvt(final Number[] price, final Number[] volume) {
		final int length = getMinLength(price, volume);
		final Number[] results = new Number[length];
		final Number[] chg = roc(price, 1, PercentageScale.RATE);
		double pvt = 0.0;
		for (int i = 0; i < length; i++) {
			if (chg[i] == null || volume[i] == null)
				continue;
			pvt = pvt + volume[i].doubleValue() * chg[i].doubleValue();
			results[i] = pvt;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからチャイキン累積出来高オシレーター (Chaikin's Volume Accumulation Oscillator) を返します。</p>
	 * 
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param volume 出来高
	 * @return チャイキン累積出来高オシレーター (Chaikin's Volume Accumulation Oscillator)
	 */
	public static Number[] vao(final Number[] high, final Number[] low, final Number[] close, final Number[] volume) {
		final int length = getMinLength(high, low, close, volume);
		final Number[] results = new Number[length];
		for (int i = 0; i < length; i++) {
			if (high[i] == null || low[i] == null || close[i] == null || volume[i] == null)
				continue;
			results[i] = volume[i].doubleValue() * (close[i].doubleValue() - high[i].doubleValue() + low[i].doubleValue()) * 0.5;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからボリュームレシオ1 (Volume Ratio A) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #vr1(Number[], Number[], int, double, double)} を
	 * 上限設定値 <code>600</code>、下限設定値 <code>150</code> で呼出すだけです。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @return ボリュームレシオ1 (Volume Ratio A)
	 * @see TechnicalAnalysis#vr1(Number[], Number[], int, double, double)
	 */
	public static Number[] vr1(final Number[] change, final Number[] volume, final int period) {
		return vr1(change, volume, period, 600, 150);
	}

	/**
	 * 指定されたデータからボリュームレシオ1 (Volume Ratio A) を返します。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @param upper 計算不可時の上限設定値
	 * @param lower 計算不可時の下限設定値
	 * @return ボリュームレシオ1 (Volume Ratio A)
	 */
	public static Number[] vr1(final Number[] change, final Number[] volume, final int period, final double upper, final double lower) {
		final int length = getMinLength(change, volume);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;

			double up = 0;
			double down = 0;
			double keep = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null || volume[j] == null)
					continue;

				final double c = change[j].doubleValue();
				final double v = volume[j].doubleValue();

				if (c > 0)
					up = up + v;
				else if (c < 0)
					down = down + v;
				else
					keep = keep + v;
			}

			final double numerator = up + keep / 2;
			final double denominator = down + keep / 2;
			if (denominator > 0)
				results[i] = numerator / denominator * 100;
			else if (numerator > 0)
				results[i] = upper;
			else
				results[i] = lower;
		}
		return results;
	}

	/**
	 * <p>指定されたデータからボリュームレシオ2 (Volume Ratio B) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #vr2(Number[], Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @return ボリュームレシオ2 (Volume Ratio B)
	 * @see TechnicalAnalysis#vr2(Number[], Number[], int, PercentageScale)
	 */
	public static Number[] vr2(final Number[] change, final Number[] volume, final int period) {
		return vr2(change, volume, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータからボリュームレシオ2 (Volume Ratio B) を返します。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return ボリュームレシオ2 (Volume Ratio B)
	 */
	public static Number[] vr2(final Number[] change, final Number[] volume, final int period, final PercentageScale unit) {
		final int length = getMinLength(change, volume);
		final Number[] results = new Number[length];
		for (int i = period - 1; i < length; i++) {
			if (change[i] == null)
				continue;

			double up = 0;
			double down = 0;
			double keep = 0;
			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null || volume[j] == null)
					continue;

				final double c = change[j].doubleValue();
				final double v = volume[j].doubleValue();

				if (c > 0)
					up = up + v;
				else if (c < 0)
					down = down + v;
				else
					keep = keep + v;
			}

			final double total = up + down + keep;
			if (total <= 0)
				results[i] = unit.scale() * 0.5;
			else
				results[i] = (up + keep * 0.5) / total * unit.scale();
		}
		return results;
	}

	/**
	 * <p>指定されたデータから和光ボリュームレシオ (Wako Volume Ratio) を返します。</p>
	 * このメソッドは利便性の為に提供しています。<br>
	 * 実装は単に {@link #wvr(Number[], Number[], int, PercentageScale)} を
	 * 百分率の尺度 {@link PercentageScale#PERCENT} で呼出すだけです。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @return 和光ボリュームレシオ (Wako Volume Ratio)
	 * @see TechnicalAnalysis#wvr(Number[], Number[], int, PercentageScale)
	 */
	public static Number[] wvr(final Number[] change, final Number[] volume, final int period) {
		return wvr(change, volume, period, PercentageScale.PERCENT);
	}

	/**
	 * 指定されたデータから和光ボリュームレシオ (Wako Volume Ratio) を返します。
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @param unit 百分率の尺度
	 * @return 和光ボリュームレシオ (Wako Volume Ratio)
	 */
	public static Number[] wvr(final Number[] change, final Number[] volume, final int period, final PercentageScale unit) {
		final int length = getMinLength(change, volume);
		final Number[] results = new Number[length];

		for (int i = period; i < length; i++) {
			if (change[i] == null)
				continue;

			double up = 0;
			double down = 0;
			double keep = 0;

			for (int j = i - period + 1; j <= i; j++) {
				if (change[j] == null || volume[j] == null)
					continue;

				final double c = change[j].doubleValue();
				final double v = volume[j].doubleValue();

				if (c > 0)
					up = up + v;
				else if (c < 0)
					down = down + v;
				else
					keep = keep + v;
			}

			final double total = up + down + keep;
			if (total <= 0)
				results[i] = 0;
			else
				results[i] = (up - down - keep) / (total) * unit.scale();
		}
		return results;
	}

	/**
	 * 指定されたデータから正出来高指数 (Positive Volume Index) を返します。
	 * 
	 * @param price 価格
	 * @param volume 出来高
	 * @return 正出来高指数 (Positive Volume Index)
	 */
	// by Norman Fosback
	public static Number[] pvi(final Number[] price, final Number[] volume) {
		final int length = getMinLength(price, volume);
		final Number[] results = new Number[length];

		double pvi = 1.0;
		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (price[i] == null || price[j] == null || volume[i] == null || volume[j] == null)
				continue;

			if (volume[i].doubleValue() > volume[j].doubleValue()) {
				final double prev_price = price[j].doubleValue();
				pvi = pvi + (price[i].doubleValue() - prev_price) / prev_price * pvi;
				results[i] = pvi;
			} else
				results[i] = pvi;
		}
		return results;
	}

	/**
	 * 指定されたデータから負出来高指数 (Negative Volume Index) を返します。
	 * 
	 * @param price 価格
	 * @param volume 出来高
	 * @return 負出来高指数 (Negative Volume Index)
	 */
	// by Norman Fosback
	public static Number[] nvi(final Number[] price, final Number[] volume) {
		final int length = getMinLength(price, volume);
		final Number[] results = new Number[length];

		double nvi = 1.0;
		for (int i = 1; i < length; i++) {
			final int j = i - 1;
			if (price[i] == null || price[j] == null || volume[i] == null || volume[j] == null)
				continue;

			if (volume[i].doubleValue() < volume[j].doubleValue()) {
				final double prev_price = price[j].doubleValue();
				nvi = nvi + (price[i].doubleValue() - prev_price) / prev_price * nvi;
				results[i] = nvi;
			} else
				results[i] = nvi;
		}
		return results;
	}

	/**
	 * <p>指定されたデータから累積騰落出来高 (On Balance Volume) を返します。</p>
	 * <pre>
	 * 当日終値＞前日終値の場合は、前日OBV＋当日出来高
	 * 当日終値＜前日終値の場合は、前日OBV－当日出来高
	 * </pre>
	 * 
	 * @param change 前日比
	 * @param volume 出来高
	 * @param period 期間
	 * @return 累積騰落出来高 (On Balance Volume)
	 */
	public static Number[] obv(final Number[] change, final Number[] volume, final int period) {
		final int length = getMinLength(change, volume);
		final Number[] results = new Number[length];

		double obv = 0;
		for (int i = 0; i < period; i++) {
			if (volume[i] == null)
				continue;
			obv = obv + volume[i].doubleValue();
		}

		for (int i = period; i < length; i++) {
			if (change[i] == null || volume[i] == null)
				continue;

			final double c = change[i].doubleValue();
			final double v = volume[i].doubleValue();
			if (c > 0)
				obv = obv + v;
			else if (c < 0)
				obv = obv - v;

			results[i] = obv;
		}
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 時系列 */

	/**
	 * 指定された4本値及び株式分割数データから株式分割修正した4本値を返します。
	 * 
	 * @param open 始値
	 * @param high 高値
	 * @param low 安値
	 * @param close 終値
	 * @param split 株式分割数
	 * @return 株式分割修正した4本値
	 */
	public static Map<FourPrice, Number[]> split(
			final Number[] open,
			final Number[] high,
			final Number[] low,
			final Number[] close,
			final Number[] split)
	{
		if (open == null || high == null || low == null || close == null || split == null)
			return null;

		final Number[] o = open.clone();
		final Number[] h = high.clone();
		final Number[] l = low.clone();
		final Number[] c = close.clone();
		final int length = getMinLength(o, h, l, c, split);

		for (int i = 0; i < length; i++) {
			if (split[i] == null)
				continue;
			final double n = split[i].doubleValue();
			for (int j = 0; j < i; j++) {
				if (o[j] != null) o[j] = o[j].doubleValue() / n;
				if (h[j] != null) h[j] = h[j].doubleValue() / n;
				if (l[j] != null) l[j] = l[j].doubleValue() / n;
				if (c[j] != null) c[j] = c[j].doubleValue() / n;
			}
		}

		final Map<FourPrice, Number[]> results = new EnumMap<FourPrice, Number[]>(FourPrice.class);
		results.put(FourPrice.OPEN, o);
		results.put(FourPrice.HIGH, h);
		results.put(FourPrice.LOW, l);
		results.put(FourPrice.CLOSE, c);
		return results;
	}

	/* ---------------------------------------------------------------------- */
	/* 非時系列 */

	/**
	 * 指定されたデータからポイント&amp;フィギュアの非時系列データを返します。
	 * 
	 * @param date 日時データ
	 * @param x データ
	 * @param point 1ポイントの単位
	 * @param reversal 転換ルール
	 * @return ポイント&amp;フィギュアの値幅情報リスト
	 */
	public static List<Step> pf(final Date[] date, final Number[] x, final double point, final int reversal) {
		final int start = findStartIndex(date, x);
		if ((start + 1) >= (date.length - 1))
			return new ArrayList<Step>(0);

		final List<Step> list = new ArrayList<Step>();

		Step pf = new Step(date[start], x[start].doubleValue());
		boolean upTrend = x[start].doubleValue() < x[start + 1].doubleValue();

		for (int i = start /*+ 1*/; i < date.length; i++) {
			if (x[i] == null)
				continue;

			final double value = x[i].doubleValue();
 
			if (upTrend) {
				// ×枠
				if (value > pf.high) {
					// 高値を更新します。
					pf.highDate = date[i];
					pf.high = value;
				} else {
					final double _low = pf.high - Math.floor(pf.high % point) - point * (reversal - 1);
					if (value < _low && pf.low < _low) {
						// 陰転
						list.add(pf);

						final Date highDate = pf.highDate;
						final double high = pf.high;

						pf = new Step(highDate, high, 0);
						pf.lowDate = date[i];
						pf.low = value;

						upTrend = false;
					}
				}
			} else {
				// ○枠
				if (value <= pf.low) {
					// 高値を更新します。
					pf.lowDate = date[i];
					pf.low = value;
				} else {
					final double _high = pf.low - Math.floor(pf.low % point) + point * reversal;
					if (value >= _high && pf.high >= _high) {
						// 陽転
						list.add(pf);

						final Date lowDate = pf.lowDate;
						final double low = pf.low;

						pf = new Step(lowDate, low, 0);
						pf.highDate = date[i];
						pf.high = value;

						upTrend = true;
					}
				}
			}

			pf.closeDate = date[i];
			pf.close = value;
			pf.period = pf.period + 1;
		}

		list.add(pf);

		return list;
	}

	/**
	 * 指定されたデータから定率法カギ足 (値幅足) の非時系列データを返します。
	 * 
	 * @param date 日時データ
	 * @param x データ
	 * @param rate 定率値幅(パーセント)
	 * @return 定率法カギ足 (値幅足) の値幅情報リスト
	 */
	public static List<Step> kagi(final Date[] date, final Number[] x, final double rate) {
		final double r = rate * 0.01;
		final List<Step> list = new ArrayList<Step>();

		final int start = findStartIndex(date, x);
		if ((start + 1) >= (date.length - 1))
			return new ArrayList<Step>(0);

		Step kagi = new Step(date[start], x[start].doubleValue());
		boolean upTrend = x[start].doubleValue() < x[start + 1].doubleValue();
		
		for (int i = start + 1; i < date.length; i++) {
			if (x[i] == null)
				continue;

			final double value = x[i].doubleValue();

			if (upTrend) {
				if (value > kagi.high) {
					// 高値を更新します。
					kagi.highDate = date[i];
					kagi.high = value;
				} else if (r <= ((kagi.high - value) / kagi.high)) {
					// 陰転
					list.add(kagi);

					final Date highDate = kagi.highDate;
					final double high = kagi.high;
					kagi = new Step(date[i], value, 0);
					kagi.open = high;
					kagi.highDate = highDate;
					kagi.high = high;

					upTrend = false;
				}
			} else {
				if (value < kagi.low) {
					// 安値を更新します。
					kagi.lowDate = date[i];
					kagi.low = value;
				} else if (r <= ((value - kagi.low) / kagi.low)) {
					// 陽転
					list.add(kagi);

					final Date lowDate = kagi.lowDate;
					final double low = kagi.low;
					kagi = new Step(date[i], value, 0);
					kagi.open = low;
					kagi.lowDate = lowDate;
					kagi.low = low;

					upTrend = true;
				}
			}

			kagi.closeDate = date[i];
			kagi.close = value;
			kagi.period = kagi.period + 1;
		}

		list.add(kagi);

		return list;
	}

	/**
	 * 指定されたデータから練行足 (練り足) の非時系列データを返します。
	 * 
	 * @param date 日時データ
	 * @param x データ
	 * @param point 値幅
	 * @return 練行足 (練り足) の値幅情報リスト
	 */
	public static List<Step> renkoh(final Date[] date, final Number[] x, final double point) {
		final List<Step> list = new ArrayList<Step>();

		final int start = findStartIndex(date, x);
		if ((start + 1) >= (date.length - 1))
			return new ArrayList<Step>(0);

		Step renkoh = new Step(date[start], x[start].doubleValue(), 0);
		boolean upTrend = x[start].doubleValue() < x[start + 1].doubleValue();

		for (int i = start + 1; i < date.length; i++) {
			if (x[i] == null)
				continue;

			final double value = x[i].doubleValue();

			if (upTrend) {
				if (value > (renkoh.high + point)) {
					for (int j = 0; j < (int) Math.floor((value - renkoh.high) / point); j++) {
						final double high = renkoh.high + point;
						renkoh.highDate = date[i];
						renkoh.high = high;
						renkoh.closeDate = date[i];
						renkoh.close = high;
						list.add(renkoh);
						renkoh = new Step(date[i], high, 0);
					}
				} else if (value < (renkoh.low - point * 2)) {
					// 陰転
					final double v = renkoh.high - point;
					renkoh = new Step(date[i], v, 0);
					if (list.size() > 0) {
						final Step o = list.get(list.size() - 1);
						renkoh.highDate = o.openDate;
					}

					for (int j = 0; j < (int) Math.floor((renkoh.high - value) / point); j++) {
						final double low = renkoh.low - point;
						renkoh.lowDate = date[i];
						renkoh.low = low;
						renkoh.closeDate = date[i];
						renkoh.close = low;
						list.add(renkoh);
						renkoh = new Step(date[i], low, 0);
					}
					upTrend = false;
				}
			} else {
				if (value < (renkoh.low - point)) {
					for (int j = 0; j < (int) Math.floor((renkoh.low - value) / point); j++) {
						final double low = renkoh.low - point;
						renkoh.lowDate = date[i];
						renkoh.low = low;
						renkoh.closeDate = date[i];
						renkoh.close = low;
						list.add(renkoh);
						renkoh = new Step(date[i], low, 0);
					}
				} else if (value > (renkoh.high + point * 2)) {
					// 陽転
					final double v = renkoh.low + point;
					renkoh = new Step(date[i], v, 0);
					if (list.size() > 0) {
						final Step o = list.get(list.size() - 1);
						renkoh.lowDate = o.openDate;
					}

					for (int j = 0; j < (int) Math.floor((value - renkoh.low) / point); j++) {
						final double high = renkoh.high + point;
						renkoh.highDate = date[i];
						renkoh.high = high;
						renkoh.closeDate = date[i];
						renkoh.close = high;
						list.add(renkoh);
						renkoh = new Step(date[i], high, 0);
					}
					upTrend = true;
				}
			}
		}

		return list;
	}

	/**
	 * 指定されたデータから新値足の非時系列データを返します。
	 * 
	 * @param date 日時データ
	 * @param x データ
	 * @param period 期間(転換ルール)
	 * @return 新値足用の値幅情報リスト
	 */
	public static List<Step> shinne(final Date[] date, final Number[] x, final int period) {
		final List<Step> list = new ArrayList<Step>();

		final int start = findStartIndex(date, x);
		if ((start + 1) >= (date.length - 1))
			return new ArrayList<Step>(0);

		Step shinne = new Step(date[start], x[start].doubleValue(), 0);
		boolean upTrend = x[start].doubleValue() < x[start + 1].doubleValue();

		for (int i = start + 1; i < date.length; i++) {
			if (x[i] == null)
				continue;

			final double value = x[i].doubleValue();

			Number over = null;
			Date overDate = null;

			for (int j = list.size() - period; j < list.size(); j++) {
				if (j < 0)
					continue;

				final Step prev = list.get(j);
				if (upTrend) {
					if (over == null || over.doubleValue() > prev.low) {
						over = prev.low;
						overDate = prev.lowDate;
					}
				} else {
					if (over == null || over.doubleValue() < prev.high) {
						over = prev.high;
						overDate = prev.highDate;
					}
				}
			}

			if (over == null)
				over = shinne.close;

			if (upTrend) {
				if (value > shinne.high) {
					// 高値を更新します。
					shinne.highDate = date[i];
					shinne.high = value;
					shinne.closeDate = date[i];
					shinne.close = value;
					shinne.period = shinne.period + 1;
					list.add(shinne);

					shinne = new Step(date[i], value, 0);
				} else if (value < over.doubleValue()) {
					// 陰転
					if (list.size() <= 0) {
						shinne.open = over.doubleValue();
						shinne.highDate = overDate;
						shinne.high = over.doubleValue();
					} else {
						final Step o = list.get(list.size() - 1);
						shinne.open = o.open;
						shinne.highDate = o.openDate;
						shinne.high = o.open;
					}

					shinne.lowDate = date[i];
					shinne.low = value;
					shinne.closeDate = date[i];
					shinne.close = value;
					shinne.period = shinne.period + 1;
					list.add(shinne);

					shinne = new Step(date[i], value, 0);
					upTrend = false;
				}
			} else {
				if (value < shinne.low) {
					// 安値を更新します。
					shinne.lowDate = date[i];
					shinne.low = value;
					shinne.closeDate = date[i];
					shinne.close = value;
					shinne.period = shinne.period + 1;
					list.add(shinne);

					shinne = new Step(date[i], value, 0);
				} else if (value > over.doubleValue()) {
					// 陽転
					if (list.size() <= 0) {
						shinne.open = over.doubleValue();
						shinne.lowDate = overDate;
						shinne.low = over.doubleValue();
					} else {
						final Step o = list.get(list.size() - 1);
						shinne.open = o.open;
						shinne.lowDate = o.openDate;
						shinne.low = o.open;
					}

					shinne.highDate = date[i];
					shinne.high = value;
					shinne.closeDate = date[i];
					shinne.close = value;
					shinne.period = shinne.period + 1;
					list.add(shinne);

					shinne = new Step(date[i], value, 0);
					upTrend = true;
				}
			}
		}

		return list;
	}

	private static int findStartIndex(final Date[] date, final Number[] x) {
		final int length = getMinLength(date, x);
		for (int i = 0; i < length; i++) {
			if (date[i] != null && x[i] != null) {
				return i;
			}
		}
		return ArrayDataUtils.INDEX_NOT_FOUND;
	}

}
