# Copyright 2019 The MACE Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import numpy as np


class QuantizeStat(object):
    def __init__(self):
        pass

    @staticmethod
    def run(log_file, percentile, enhance, enhance_ratio):
        res = {}
        tensor_id = {}
        idx = 0
        tensor_ranges = {}
        with open(log_file) as log:
            for line in log:
                if line.find("Tensor range @@") != -1:
                    tensor_name, minmax = line.split("@@")[1:]
                    min_val, max_val = [float(i) for i in
                                        minmax.strip().split(",")]
                    if tensor_name not in tensor_ranges:
                        tensor_ranges[tensor_name] = ([], [])
                    tensor_ranges[tensor_name][0].append(min_val)
                    tensor_ranges[tensor_name][1].append(max_val)
                    if tensor_name not in tensor_id:
                        tensor_id[tensor_name] = idx
                        idx = idx + 1

        for tensor_name in tensor_ranges:
            samples = len(tensor_ranges[tensor_name][0])
            tensor_min = np.percentile(tensor_ranges[tensor_name][0],
                                       percentile)
            tensor_max = np.percentile(tensor_ranges[tensor_name][1],
                                       100 - percentile)
            assert tensor_min < tensor_max
            if not enhance or samples <= 1:
                res[tensor_name] = (tensor_min, tensor_max)
            else:
                """
                Enhancement mode:
                This policy eliminates outliers that cause long-tail
                statistical range. We try to reduce as much range as it could
                while retaining more samples. d(range)/d(sample_quantile) is
                used to measure this qualitatively.
                """
                tensor_mins = np.sort(tensor_ranges[tensor_name][0])
                tensor_maxs = np.sort(tensor_ranges[tensor_name][1])[::-1]
                cur_min_idx = 0
                cur_max_idx = 0
                cur_min = tensor_min
                cur_max = tensor_max
                for i in range(samples):
                    if tensor_mins[i] + 0.1 > cur_max:
                        break

                    delta_range = (tensor_mins[i] - cur_min) / (cur_max - cur_min)  # noqa
                    delta_quantile = float(i - cur_min_idx) / (samples - cur_min_idx)  # noqa
                    if delta_quantile > 0 and delta_range / delta_quantile > enhance_ratio:  # noqa
                        cur_min_idx = i
                        cur_min = tensor_mins[i]

                    if cur_min + 0.1 > tensor_maxs[i]:
                        break

                    delta_range = (cur_max - tensor_maxs[i]) / (cur_max - cur_min)  # noqa
                    delta_quantile = float(i - cur_max_idx) / (samples - cur_max_idx)  # noqa
                    if delta_quantile > 0 and delta_range / delta_quantile > enhance_ratio:  # noqa
                        cur_max_idx = i
                        cur_max = tensor_maxs[i]

                res[tensor_name] = (cur_min, cur_max)

        res_list = [(name, rng) for (name, rng) in res.items()]
        res_list.sort(key=lambda x: tensor_id[x[0]])

        return res_list


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--log_file",
        type=str,
        default="",
        help="path of log file that records tensor range")
    parser.add_argument(
        "--percentile",
        type=int,
        default=0,
        help="range percentile")
    parser.add_argument(
        "--enhance",
        action="store_true",
        help="range percentile")
    parser.add_argument(
        "--enhance_ratio",
        type=int,
        default=10,
        help="enhance ratio")
    FLAGS, unparsed = parser.parse_known_args()

    res = QuantizeStat.run(FLAGS.log_file, FLAGS.percentile, FLAGS.enhance,
                           FLAGS.enhance_ratio)
    for r in res:
        print("%s@@%f,%f" % (r[0], r[1][0], r[1][1]))
