#!/usr/bin/python

# tz_random.py
#       --copyright--                   Copyright 2009 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       September 6, 2009       bar
#       September 7, 2009       bar
#       November 12, 2011       bar     take the s out of the url
#       November 29, 2011       bar     pyflake cleanup
#       --eodstamps--
##      \file
#
#
#       Do random things.
#
#

import  random
import  types

import  tzlib


class   a_limit(object) :

    def __init__(me, name, lo, hi) :
        me.name = name
        me.lo   = lo
        me.hi   = hi

    pass        # a_limit



class   a_random_object_tweaker(object) :

    def __init__(me, ob, limits = [], do_not_do = []) :
        me.ob           = ob

        limits          = limits    or []
        me.limits       = dict([ [ lm.name, lm ] for lm in limits ])

        do_not_do       = do_not_do or []

        vals            = tzlib.make_dictionary([ eln for eln in dir(me.ob) if (type(getattr(me.ob, eln)) == types.IntType) or (type(getattr(me.ob, eln)) == types.FloatType) ])

        for n in do_not_do :
            if n in vals :
                del(vals[n])
            pass

        me.vals         = vals

        me.original     = dict([ [ n, getattr(me.ob, n) ] for n in me.vals.keys() ])

        # print [ str(v) for v in me.vals ]



    def tweak(me)   :
        if  (random.random() < (1.0 / len(me.original))) and len(me.get_changed_values()) :
            me.eln  = random.choice(me.get_changed_values().keys())
            v       = me.original[me.eln]
        else        :
            me.eln  = random.choice(me.vals.keys())
            v       = None
        me.ov       = getattr(me.ob, me.eln)
        if  me.eln in me.limits :
            lm      = me.limits[me.eln]
            if  v  == None :
                v   = lm.lo + (random.random() * (lm.hi - lm.lo))
            setattr(me.ob, me.eln, v)

            return(True)

        return(False)


    def untweak(me) :
        setattr(me.ob, me.eln, me.ov)


    def learn_tweak(me) :
        pass


    def get_changed_values(me) :
        return(dict([ [ n, getattr(me.ob, n) ] for n in me.vals.keys() if (getattr(me.ob, n) != me.original[n]) ]))


    def set_values(me, vals) :
        for n in vals.keys() :
            setattr(me.ob, n, vals[n])
        pass


    def set_original(me) :
        me.set_values(me.original)


    def formatted_str(me, format = None) :
        format          = format or "%s=%.1f"
        vals            = me.get_changed_values()
        kys             = vals.keys()
        kys.sort()
        return("\n".join([ format % ( n, vals[n] ) for n in kys ]) + "\n")


    def __str__(me) :
        vals            = me.get_changed_values()
        kys             = vals.keys()
        kys.sort()
        return("\n".join([ "%s=%.1f" % ( n, vals[n] ) for n in kys ]) + "\n")


    pass                # a_random_object_tweaker




class   a_weighted_random_items(object) :

    class   an_item(object) :
        def __init__(me, wate, val) :
            me.wate     = wate
            me.val      = val
        pass        # an_item


    def __init__(me, wate = None, val = None) :
        me.items        = []
        me.wate         = 0.0

        if  wate != None :
            me.add_item(wate, val)
        pass


    def add_item(me, wate, val = None) :

        try             :
            w           = wate[0]
        except TypeError :
            wate        = [ wate ]

        if  isinstance(val, basestring) :
            val         = [ val ]
        else            :
            try         :
                v       = val[0]
            except TypeError :
                val     = [ val ]
            pass

        if  len(wate)   < len(val) :
            raise       ValueError, "len(wate)==%u  len(val)==%u !" % ( len(wate), len(val) )

        wi              = 0
        for w in wate   :
            w           = float(w)
            if  w      <= 0.0 :
                raise   ValueError, "Bad weight value [%f]. Must be above zero!" % w

            me.wate    += w

            try         :
                v       = val[wi]
                if  v  == None :
                    v   = wi
                pass
            except IndexError :
                v       = wi

            me.items.append(me.an_item(me.wate, v))

            wi         += 1

        pass


    def choose_item(me) :
        if  not me.wate :
            return(None)

        r   = random.random() * me.wate


        i   = tzlib.binary_search(me.items, r, cmp_rtn = lambda a, i, item, cmp_obj : cmp(a[i].wate, item))

        return(me.items[i].val)


    pass    # a_weighted_random_items




help_str    = """
%s how_many item weight (item weight)...

Print 'how_many' items chosen randomly, distributed by weights.
"""

if  __name__ == '__main__' :

    import  os
    import  sys

    import  TZCommandLineAtFile


    program_name    = sys.argv.pop(0)
    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)


    me              = a_weighted_random_items()


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--help", "-h", "-?", "/?", "/h", "/H" ] )
        if  oi < 0 :    break
        del sys.argv[oi]
        print help_str % ( os.path.basename(program_name) )

        sys.exit(254)


    if  len(sys.argv) < 3 :
        print "Please tell me how many items to choose and at least 1 item!"
        sys.exit(101)

    how_many        = max(0, int(sys.argv.pop(0)), 0)

    if  len(sys.argv) & 1 :
        print "The items are pairs of string and weight. There seems to be an odd number of item parameters!"
        sys.exit(102)


    if  random.random() >= 0.5 :
        while len(sys.argv) :
            iname   =       sys.argv.pop(0)
            iwate   = float(sys.argv.pop(0))
            me.add_item(iwate, iname)
        pass
    elif random.random() >= 0.5 :
        print
        print "array"
        print
        inames      = sys.argv[0 : : 2 ]
        iwates      = [ float(w) for w in sys.argv[1 : : 2 ] ]
        me.add_item(iwates, inames)
    else :
        print
        print "no vals"
        print
        iwates      = [ float(w) for w in sys.argv[1 : : 2 ] ]
        me.add_item(iwates)


    while how_many  :
        how_many   -= 1
        print me.choose_item()

    pass

#
#
# eof

